[API] Add Create + Delete Branch API (+ Basic GIT Error Propagation) (#70)

This change adds the following:
- create / delete branch
- basic error propagation from git to user (notfound, conflict, invalidinput)
- create repo root folder in server instead of service constructor
This commit is contained in:
Johannes Batzill 2022-11-10 20:57:31 -08:00 committed by GitHub
parent 81040d0453
commit c55c53deab
57 changed files with 2579 additions and 1439 deletions

View File

@ -39,7 +39,7 @@ func (c *Client) GetBlob(ctx context.Context, params *GetBlobParams) (*GetBlobOu
SizeLimit: params.SizeLimit,
})
if err != nil {
return nil, err
return nil, processRPCErrorf(err, "failed to get blob from server")
}
blob := resp.GetBlob()

View File

@ -22,6 +22,26 @@ const (
BranchSortOptionDate
)
type CreateBranchParams struct {
// RepoUID is the uid of the git repository
RepoUID string
// BranchName is the name of the branch
BranchName string
// Target is a git reference (branch / tag / commit SHA)
Target string
}
type CreateBranchOutput struct {
Branch Branch
}
type DeleteBranchParams struct {
// RepoUID is the uid of the git repository
RepoUID string
// Name is the name of the branch
BranchName string
}
type ListBranchesParams struct {
// RepoUID is the uid of the git repository
RepoUID string
@ -43,12 +63,54 @@ type Branch struct {
Commit *Commit
}
func (c *Client) CreateBranch(ctx context.Context, params *CreateBranchParams) (*CreateBranchOutput, error) {
if params == nil {
return nil, ErrNoParamsProvided
}
resp, err := c.refService.CreateBranch(ctx, &rpc.CreateBranchRequest{
RepoUid: params.RepoUID,
Target: params.Target,
BranchName: params.BranchName,
})
if err != nil {
return nil, processRPCErrorf(err, "failed to create branch on server")
}
var branch *Branch
branch, err = mapRPCBranch(resp.GetBranch())
if err != nil {
return nil, fmt.Errorf("failed to map rpc branch: %w", err)
}
return &CreateBranchOutput{
Branch: *branch,
}, nil
}
func (c *Client) DeleteBranch(ctx context.Context, params *DeleteBranchParams) error {
if params == nil {
return ErrNoParamsProvided
}
_, err := c.refService.DeleteBranch(ctx, &rpc.DeleteBranchRequest{
RepoUid: params.RepoUID,
BranchName: params.BranchName,
// TODO: what are scenarios where we wouldn't want to force delete?
// Branch protection is a different story, and build on top application layer.
Force: true,
})
if err != nil {
return processRPCErrorf(err, "failed to delete branch on server")
}
return nil
}
func (c *Client) ListBranches(ctx context.Context, params *ListBranchesParams) (*ListBranchesOutput, error) {
if params == nil {
return nil, ErrNoParamsProvided
}
stream, err := c.repoService.ListBranches(ctx, &rpc.ListBranchesRequest{
stream, err := c.refService.ListBranches(ctx, &rpc.ListBranchesRequest{
RepoUid: params.RepoUID,
IncludeCommit: params.IncludeCommit,
Query: params.Query,
@ -73,7 +135,7 @@ func (c *Client) ListBranches(ctx context.Context, params *ListBranchesParams) (
break
}
if err != nil {
return nil, fmt.Errorf("received unexpected error from rpc: %w", err)
return nil, processRPCErrorf(err, "received unexpected error from rpc")
}
if next.GetBranch() == nil {
return nil, fmt.Errorf("expected branch message")

View File

@ -18,6 +18,7 @@ type Config struct {
type Client struct {
conn *grpc.ClientConn
repoService rpc.RepositoryServiceClient
refService rpc.ReferenceServiceClient
httpService rpc.SmartHTTPServiceClient
}
@ -30,6 +31,7 @@ func New(remoteAddr string) (*Client, error) {
return &Client{
conn: conn,
repoService: rpc.NewRepositoryServiceClient(conn),
refService: rpc.NewReferenceServiceClient(conn),
httpService: rpc.NewSmartHTTPServiceClient(conn),
}, nil
}

View File

@ -64,7 +64,7 @@ func (c *Client) ListCommits(ctx context.Context, params *ListCommitsParams) (*L
// get header first
header, err := stream.Recv()
if err != nil {
return nil, fmt.Errorf("error occured while receiving header: %w", err)
return nil, processRPCErrorf(err, "error occured while receiving header")
}
if header.GetHeader() == nil {
return nil, fmt.Errorf("header missing")
@ -84,7 +84,7 @@ func (c *Client) ListCommits(ctx context.Context, params *ListCommitsParams) (*L
break
}
if err != nil {
return nil, fmt.Errorf("received unexpected error from rpc: %w", err)
return nil, processRPCErrorf(err, "received unexpected error from server")
}
if next.GetCommit() == nil {
return nil, fmt.Errorf("expected commit message")

View File

@ -7,3 +7,6 @@ package gitrpc
import "errors"
var ErrNoParamsProvided = errors.New("no params provided")
var ErrAlreadyExists = errors.New("already exists")
var ErrInvalidArgument = errors.New("invalid argument")
var ErrNotFound = errors.New("not found")

View File

@ -16,6 +16,8 @@ type Interface interface {
GetSubmodule(ctx context.Context, params *GetSubmoduleParams) (*GetSubmoduleOutput, error)
GetBlob(ctx context.Context, params *GetBlobParams) (*GetBlobOutput, error)
ListCommits(ctx context.Context, params *ListCommitsParams) (*ListCommitsOutput, error)
CreateBranch(ctx context.Context, params *CreateBranchParams) (*CreateBranchOutput, error)
DeleteBranch(ctx context.Context, params *DeleteBranchParams) error
ListBranches(ctx context.Context, params *ListBranchesParams) (*ListBranchesOutput, error)
ListCommitTags(ctx context.Context, params *ListCommitTagsParams) (*ListCommitTagsOutput, error)

View File

@ -24,12 +24,12 @@ func (g Adapter) GetBlob(ctx context.Context, repoPath string, sha string, sizeL
giteaBlob, err := giteaRepo.GetBlob(sha)
if err != nil {
return nil, fmt.Errorf("error getting blob '%s': %w", sha, err)
return nil, processGiteaErrorf(err, "error getting blob '%s'", sha)
}
reader, err := giteaBlob.DataAsync()
if err != nil {
return nil, fmt.Errorf("error opening data for blob '%s': %w", sha, err)
return nil, processGiteaErrorf(err, "error opening data for blob '%s'", sha)
}
returnSize := giteaBlob.Size()

View File

@ -0,0 +1,75 @@
// 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"
"strings"
gitea "code.gitea.io/gitea/modules/git"
"github.com/harness/gitness/gitrpc/internal/types"
)
// CreateBranch creates a new branch.
// Note: target is the commit (or points to the commit) the branch will be pointing to.
func (g Adapter) CreateBranch(ctx context.Context, repoPath string,
branchName string, target string) (*types.Branch, error) {
giteaRepo, err := gitea.OpenRepository(ctx, repoPath)
if err != nil {
return nil, err
}
defer giteaRepo.Close()
// Get the commit object for the ref
giteaCommit, err := giteaRepo.GetCommit(target)
if err != nil {
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", target)
}
// In case of target being an annotated tag, gitea is overwriting the commit message with the tag message.
// Reload the commit explicitly in case it's a tag (independent of whether it's annotated or not to simplify code)
// NOTE: we also allow passing refs/tags/tagName or tags/tagName, which is not covered by IsTagExist.
// Worst case we have a false positive and reload the same commit, we don't want false negatives though!
if strings.HasPrefix(target, gitea.TagPrefix) || strings.HasPrefix(target, "tags") || giteaRepo.IsTagExist(target) {
giteaCommit, err = giteaRepo.GetCommit(giteaCommit.ID.String())
if err != nil {
return nil, processGiteaErrorf(err, "error getting commit for annotated tag '%s' (commitId '%s')",
target, giteaCommit.ID.String())
}
}
err = giteaRepo.CreateBranch(branchName, giteaCommit.ID.String())
if err != nil {
return nil, processGiteaErrorf(err, "failed to create branch '%s'", branchName)
}
commit, err := mapGiteaCommit(giteaCommit)
if err != nil {
return nil, fmt.Errorf("failed to map gitea commit: %w", err)
}
return &types.Branch{
Name: branchName,
SHA: giteaCommit.ID.String(),
Commit: commit,
}, nil
}
// DeleteBranch deletes an existing branch.
func (g Adapter) DeleteBranch(ctx context.Context, repoPath string, branchName string, force bool) error {
giteaRepo, err := gitea.OpenRepository(ctx, repoPath)
if err != nil {
return err
}
defer giteaRepo.Close()
err = giteaRepo.DeleteBranch(branchName, gitea.DeleteBranchOptions{Force: force})
if err != nil {
return processGiteaErrorf(err, "failed to delete branch '%s'", branchName)
}
return nil
}

View File

@ -31,7 +31,7 @@ func (g Adapter) GetLatestCommit(ctx context.Context, repoPath string,
giteaCommit, err := giteaGetCommitByPath(giteaRepo, ref, treePath)
if err != nil {
return nil, fmt.Errorf("error getting latest commit for '%s': %w", treePath, err)
return nil, processGiteaErrorf(err, "error getting latest commit for '%s'", treePath)
}
return mapGiteaCommit(giteaCommit)
@ -47,7 +47,7 @@ func giteaGetCommitByPath(giteaRepo *gitea.Repository, ref string, treePath stri
stdout, _, runErr := gitea.NewCommand(giteaRepo.Ctx, "log", ref, "-1", giteaPrettyLogFormat, "--", treePath).
RunStdBytes(&gitea.RunOpts{Dir: giteaRepo.Path})
if runErr != nil {
return nil, runErr
return nil, fmt.Errorf("failed to trigger log command: %w", runErr)
}
giteaCommits, err := giteaParsePrettyFormatLogToList(giteaRepo, stdout)
@ -70,7 +70,7 @@ func giteaParsePrettyFormatLogToList(giteaRepo *gitea.Repository, logs []byte) (
for _, commitID := range parts {
commit, err := giteaRepo.GetCommit(string(commitID))
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get commit '%s': %w", string(commitID), err)
}
giteaCommits = append(giteaCommits, commit)
}
@ -91,17 +91,17 @@ func (g Adapter) ListCommits(ctx context.Context, repoPath string,
// Get the giteaTopCommit object for the ref
giteaTopCommit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, 0, fmt.Errorf("error getting commit for ref '%s': %w", ref, err)
return nil, 0, processGiteaErrorf(err, "error getting commit top commit for ref '%s'", ref)
}
giteaCommits, err := giteaTopCommit.CommitsByRange(page, pageSize)
if err != nil {
return nil, 0, fmt.Errorf("error getting commits: %w", err)
return nil, 0, processGiteaErrorf(err, "error getting commits")
}
totalCount, err := giteaTopCommit.CommitsCount()
if err != nil {
return nil, 0, fmt.Errorf("error getting total commit count: %w", err)
return nil, 0, processGiteaErrorf(err, "error getting total commit count")
}
commits := make([]types.Commit, len(giteaCommits))
@ -129,7 +129,7 @@ func (g Adapter) GetCommit(ctx context.Context, repoPath string, ref string) (*t
commit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, err
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
}
return mapGiteaCommit(commit)
@ -149,7 +149,7 @@ func (g Adapter) GetCommits(ctx context.Context, repoPath string, refs []string)
var giteaCommit *gitea.Commit
giteaCommit, err = giteaRepo.GetCommit(sha)
if err != nil {
return nil, err
return nil, processGiteaErrorf(err, "error getting commit '%s'", sha)
}
var commit *types.Commit

View File

@ -5,13 +5,59 @@
package gitea
import (
"errors"
"fmt"
"strings"
gitea "code.gitea.io/gitea/modules/git"
"github.com/harness/gitness/gitrpc/internal/types"
"github.com/rs/zerolog/log"
)
// Logs the error and message, returns either the provided message or a git equivalent if possible.
// Always logs the full message with error as warning.
func processGiteaErrorf(err error, format string, args ...interface{}) error {
// create fallback error returned if we can't map it
fallbackErr := fmt.Errorf(format, args...)
// always log internal error together with message.
log.Warn().Msgf("%v: [GITEA] %v", fallbackErr, err)
// check if it's a RunStdError error (contains raw git error)
var runStdErr gitea.RunStdError
if errors.As(err, &runStdErr) {
return mapGiteaRunStdError(runStdErr, fallbackErr)
}
switch {
case gitea.IsErrNotExist(err):
return types.ErrNotFound
default:
return fallbackErr
}
}
// TODO: Improve gitea error handling.
// Doubt this will work for all std errors, as git doesn't seem to have nice error codes.
func mapGiteaRunStdError(err gitea.RunStdError, fallback error) error {
switch {
// exit status 128 - fatal: A branch named 'mybranch' already exists.
// exit status 128 - fatal: cannot lock ref 'refs/heads/a': 'refs/heads/a/b' exists; cannot create 'refs/heads/a'
case err.IsExitCode(128) && strings.Contains(err.Stderr(), "exists"):
return types.ErrAlreadyExists
// exit status 128 - fatal: 'a/bc/d/' is not a valid branch name.
case err.IsExitCode(128) && strings.Contains(err.Stderr(), "not a valid"):
return types.ErrInvalidArgument
// exit status 1 - error: branch 'mybranch' not found.
case err.IsExitCode(1) && strings.Contains(err.Stderr(), "not found"):
return types.ErrNotFound
default:
return fallback
}
}
func mapGiteaRawRef(raw map[string]string) (map[types.GitReferenceField]string, error) {
res := make(map[types.GitReferenceField]string, len(raw))
for k, v := range raw {
@ -57,7 +103,7 @@ func mapGiteaCommit(giteaCommit *gitea.Commit) (*types.Commit, error) {
return nil, fmt.Errorf("failed to map gitea commiter: %w", err)
}
return &types.Commit{
Sha: giteaCommit.ID.String(),
SHA: giteaCommit.ID.String(),
Title: giteaCommit.Summary(),
// remove potential tailing newlines from message
Message: strings.TrimRight(giteaCommit.Message(), "\n"),

View File

@ -119,7 +119,7 @@ func walkGiteaReferenceParser(parser *gitearef.Parser, handler types.WalkReferen
}
if err := parser.Err(); err != nil {
return fmt.Errorf("failed to parse output: %w", err)
return processGiteaErrorf(err, "failed to parse reference walk output")
}
return nil

View File

@ -35,14 +35,14 @@ func (g Adapter) SetDefaultBranch(ctx context.Context, repoPath string,
// change default branch
err = giteaRepo.SetDefaultBranch(defaultBranch)
if err != nil {
return fmt.Errorf("failed to set new default branch: %w", err)
return processGiteaErrorf(err, "failed to set new default branch")
}
return nil
}
func (g Adapter) Clone(ctx context.Context, from, to string, opts types.CloneRepoOptions) error {
return gitea.Clone(ctx, from, to, gitea.CloneRepoOptions{
err := gitea.Clone(ctx, from, to, gitea.CloneRepoOptions{
Timeout: opts.Timeout,
Mirror: opts.Mirror,
Bare: opts.Bare,
@ -54,14 +54,24 @@ func (g Adapter) Clone(ctx context.Context, from, to string, opts types.CloneRep
Filter: opts.Filter,
SkipTLSVerify: opts.SkipTLSVerify,
})
if err != nil {
return processGiteaErrorf(err, "failed to clone repo")
}
return nil
}
func (g Adapter) AddFiles(repoPath string, all bool, files ...string) error {
return gitea.AddChanges(repoPath, all, files...)
err := gitea.AddChanges(repoPath, all, files...)
if err != nil {
return processGiteaErrorf(err, "failed to add changes")
}
return nil
}
func (g Adapter) Commit(repoPath string, opts types.CommitChangesOptions) error {
return gitea.CommitChanges(repoPath, gitea.CommitChangesOptions{
err := gitea.CommitChanges(repoPath, gitea.CommitChangesOptions{
Committer: &gitea.Signature{
Name: opts.Committer.Identity.Name,
Email: opts.Committer.Identity.Email,
@ -74,10 +84,15 @@ func (g Adapter) Commit(repoPath string, opts types.CommitChangesOptions) error
},
Message: opts.Message,
})
if err != nil {
return processGiteaErrorf(err, "failed to commit changes")
}
return nil
}
func (g Adapter) Push(ctx context.Context, repoPath string, opts types.PushOptions) error {
return gitea.Push(ctx, repoPath, gitea.PushOptions{
err := gitea.Push(ctx, repoPath, gitea.PushOptions{
Remote: opts.Remote,
Branch: opts.Branch,
Force: opts.Force,
@ -85,4 +100,9 @@ func (g Adapter) Push(ctx context.Context, repoPath string, opts types.PushOptio
Env: opts.Env,
Timeout: opts.Timeout,
})
if err != nil {
return processGiteaErrorf(err, "failed to push changes")
}
return nil
}

View File

@ -6,7 +6,6 @@ package gitea
import (
"context"
"fmt"
gitea "code.gitea.io/gitea/modules/git"
"github.com/harness/gitness/gitrpc/internal/types"
@ -27,12 +26,12 @@ func (g Adapter) GetSubmodule(ctx context.Context, repoPath string,
// Get the giteaCommit object for the ref
giteaCommit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, fmt.Errorf("error getting commit for ref '%s': %w", ref, err)
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
}
giteaSubmodule, err := giteaCommit.GetSubModule(treePath)
if err != nil {
return nil, fmt.Errorf("error getting submodule '%s' from commit: %w", ref, err)
return nil, processGiteaErrorf(err, "error getting submodule '%s' from commit", treePath)
}
return &types.Submodule{

View File

@ -31,7 +31,12 @@ func (g Adapter) GetAnnotatedTag(ctx context.Context, repoPath string, sha strin
}
defer giteaRepo.Close()
return giteaGetAnnotatedTag(giteaRepo, sha)
tag, err := giteaGetAnnotatedTag(giteaRepo, sha)
if err != nil {
return nil, processGiteaErrorf(err, "failed to get annotated tag with sha '%s'", sha)
}
return tag, nil
}
// GetAnnotatedTags returns the tags for a specific list of tag sha.
@ -47,7 +52,7 @@ func (g Adapter) GetAnnotatedTags(ctx context.Context, repoPath string, shas []s
var tag *types.Tag
tag, err = giteaGetAnnotatedTag(giteaRepo, sha)
if err != nil {
return nil, err
return nil, processGiteaErrorf(err, "failed to get annotated tag with sha '%s'", sha)
}
tags[i] = *tag

View File

@ -34,13 +34,14 @@ func (g Adapter) GetTreeNode(ctx context.Context, repoPath string,
// Get the giteaCommit object for the ref
giteaCommit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, fmt.Errorf("error getting commit for ref '%s': %w", ref, err)
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
}
// TODO: handle ErrNotExist :)
giteaTreeEntry, err := giteaCommit.GetTreeEntryByPath(treePath)
if err != nil {
return nil, err
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())
@ -83,13 +84,13 @@ func (g Adapter) ListTreeNodes(ctx context.Context, repoPath string,
// Get the giteaCommit object for the ref
giteaCommit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, fmt.Errorf("error getting commit for ref '%s': %w", ref, err)
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, fmt.Errorf("error getting tree for '%s': %w", treePath, err)
return nil, processGiteaErrorf(err, "error getting tree for '%s'", treePath)
}
var giteaEntries gitea.Entries
@ -99,7 +100,7 @@ func (g Adapter) ListTreeNodes(ctx context.Context, repoPath string,
giteaEntries, err = giteaTree.ListEntries()
}
if err != nil {
return nil, fmt.Errorf("failed to list entries for tree '%s': %w", treePath, err)
return nil, processGiteaErrorf(err, "failed to list entries for tree '%s'", treePath)
}
var latestCommits []gitea.CommitInfo
@ -107,7 +108,7 @@ func (g Adapter) ListTreeNodes(ctx context.Context, repoPath string,
// TODO: can be speed up with latestCommitCache (currently nil)
latestCommits, _, err = giteaEntries.GetCommitsInfo(ctx, giteaCommit, treePath, nil)
if err != nil {
return nil, fmt.Errorf("failed to get latest commits for entries: %w", err)
return nil, processGiteaErrorf(err, "failed to get latest commits for entries")
}
if len(latestCommits) != len(giteaEntries) {

View File

@ -11,11 +11,11 @@ import (
)
func (s RepositoryService) GetBlob(ctx context.Context, request *rpc.GetBlobRequest) (*rpc.GetBlobResponse, error) {
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
// TODO: do we need to validate request for nil?
gitBlob, err := s.adapter.GetBlob(ctx, repoPath, request.GetSha(), request.GetSizeLimit())
if err != nil {
return nil, err
return nil, processGitErrorf(err, "failed to get blob")
}
return &rpc.GetBlobResponse{

View File

@ -18,10 +18,42 @@ import (
var listBranchesRefFields = []types.GitReferenceField{types.GitReferenceFieldRefName, types.GitReferenceFieldObjectName}
func (s RepositoryService) ListBranches(request *rpc.ListBranchesRequest,
stream rpc.RepositoryService_ListBranchesServer) error {
func (s ReferenceService) CreateBranch(ctx context.Context,
request *rpc.CreateBranchRequest) (*rpc.CreateBranchResponse, error) {
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
gitBranch, err := s.adapter.CreateBranch(ctx, repoPath, request.GetBranchName(), request.GetTarget())
if err != nil {
return nil, processGitErrorf(err, "failed to create branch")
}
branch, err := mapGitBranch(gitBranch)
if err != nil {
return nil, err
}
return &rpc.CreateBranchResponse{
Branch: branch,
}, nil
}
func (s ReferenceService) DeleteBranch(ctx context.Context,
request *rpc.DeleteBranchRequest) (*rpc.DeleteBranchResponse, error) {
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
// TODO: block deletion of protected branch (in the future)
err := s.adapter.DeleteBranch(ctx, repoPath, request.GetBranchName(), request.GetForce())
if err != nil {
return nil, processGitErrorf(err, "failed to delete branch")
}
return &rpc.DeleteBranchResponse{}, nil
}
func (s ReferenceService) ListBranches(request *rpc.ListBranchesRequest,
stream rpc.ReferenceService_ListBranchesServer) error {
ctx := stream.Context()
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
// get all required information from git refrences
branches, err := s.listBranchesLoadReferenceData(ctx, repoPath, request)
@ -63,7 +95,7 @@ func (s RepositoryService) ListBranches(request *rpc.ListBranchesRequest,
return nil
}
func (s RepositoryService) listBranchesLoadReferenceData(ctx context.Context,
func (s ReferenceService) listBranchesLoadReferenceData(ctx context.Context,
repoPath string, request *rpc.ListBranchesRequest) ([]*rpc.Branch, error) {
// TODO: can we be smarter with slice allocation
branches := make([]*rpc.Branch, 0, 16)
@ -88,7 +120,7 @@ func (s RepositoryService) listBranchesLoadReferenceData(ctx context.Context,
err = s.adapter.WalkReferences(ctx, repoPath, handler, opts)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get branch references: %v", err)
return nil, processGitErrorf(err, "failed to walk branch references")
}
log.Trace().Msgf("git adapter returned %d branches", len(branches))

View File

@ -15,12 +15,12 @@ import (
func (s RepositoryService) ListCommits(request *rpc.ListCommitsRequest,
stream rpc.RepositoryService_ListCommitsServer) error {
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
gitCommits, totalCount, err := s.adapter.ListCommits(stream.Context(), repoPath, request.GetGitRef(),
int(request.GetPage()), int(request.GetPageSize()))
if err != nil {
return status.Errorf(codes.Internal, "failed to list commits: %v", err)
return processGitErrorf(err, "failed to get list of commits")
}
log.Trace().Msgf("git adapter returned %d commits (total: %d)", len(gitCommits), totalCount)
@ -61,7 +61,7 @@ func (s RepositoryService) getLatestCommit(ctx context.Context, repoPath string,
ref string, path string) (*rpc.Commit, error) {
gitCommit, err := s.adapter.GetLatestCommit(ctx, repoPath, ref, path)
if err != nil {
return nil, err
return nil, processGitErrorf(err, "failed to get latest commit")
}
return mapGitCommit(gitCommit)

View File

@ -7,11 +7,9 @@ package service
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
@ -38,30 +36,13 @@ type SmartHTTPService struct {
reposRoot string
}
func NewHTTPService(adapter GitAdapter, gitRoot string) (*SmartHTTPService, error) {
reposRoot := filepath.Join(gitRoot, repoSubdirName)
if _, err := os.Stat(reposRoot); errors.Is(err, os.ErrNotExist) {
if err = os.MkdirAll(reposRoot, 0o700); err != nil {
return nil, err
}
}
func NewHTTPService(adapter GitAdapter, reposRoot string) (*SmartHTTPService, error) {
return &SmartHTTPService{
adapter: adapter,
reposRoot: reposRoot,
}, nil
}
func (s *SmartHTTPService) getFullPathForRepo(uid string) string {
// split repos into subfolders using their prefix to distribute repos accross a set of folders.
return filepath.Join(
s.reposRoot, // root folder
uid[0:2], // first subfolder
uid[2:4], // second subfolder
fmt.Sprintf("%s.%s", uid[4:], gitRepoSuffix), // remainder with .git
)
}
func (s *SmartHTTPService) InfoRefs(
r *rpc.InfoRefsRequest,
stream rpc.SmartHTTPService_InfoRefsServer,
@ -72,7 +53,7 @@ func (s *SmartHTTPService) InfoRefs(
environ = append(environ, "GIT_PROTOCOL="+r.GitProtocol)
}
repoPath := s.getFullPathForRepo(r.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, r.GetRepoUid())
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
@ -120,7 +101,7 @@ func (s *SmartHTTPService) ServicePack(stream rpc.SmartHTTPService_ServicePackSe
return status.Errorf(codes.InvalidArgument, "PostUploadPack(): repository UID is missing")
}
repoPath := s.getFullPathForRepo(req.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, req.GetRepoUid())
stdin := streamio.NewReader(func() ([]byte, error) {
resp, streamErr := stream.Recv()

View File

@ -32,4 +32,6 @@ type GitAdapter interface {
GetLatestCommit(ctx context.Context, repoPath string, ref string, treePath string) (*types.Commit, error)
GetAnnotatedTag(ctx context.Context, repoPath string, sha string) (*types.Tag, error)
GetAnnotatedTags(ctx context.Context, repoPath string, shas []string) ([]types.Tag, error)
CreateBranch(ctx context.Context, repoPath string, branchName string, target string) (*types.Branch, error)
DeleteBranch(ctx context.Context, repoPath string, branchName string, force bool) error
}

View File

@ -5,12 +5,37 @@
package service
import (
"errors"
"fmt"
"github.com/harness/gitness/gitrpc/internal/types"
"github.com/harness/gitness/gitrpc/rpc"
"github.com/rs/zerolog/log"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Logs the error and message, returns either the provided message or a rpc equivalent if possible.
// Always logs the full message with error as warning.
func processGitErrorf(err error, format string, args ...interface{}) error {
// create fallback error returned if we can't map it
message := fmt.Sprintf(format, args...)
// always log internal error together with message.
log.Warn().Msgf("%s: [GIT] %v", message, err)
switch {
case errors.Is(err, types.ErrNotFound):
return status.Error(codes.NotFound, message)
case errors.Is(err, types.ErrAlreadyExists):
return status.Errorf(codes.AlreadyExists, message)
case errors.Is(err, types.ErrInvalidArgument):
return status.Errorf(codes.InvalidArgument, message)
default:
return status.Errorf(codes.Unknown, message)
}
}
func mapSortOrder(s rpc.SortOrder) types.SortOrder {
switch s {
case rpc.SortOrder_Asc:
@ -63,13 +88,34 @@ func mapGitMode(m types.TreeNodeMode) rpc.TreeNodeMode {
return rpc.TreeNodeMode(m)
}
func mapGitBranch(gitBranch *types.Branch) (*rpc.Branch, error) {
if gitBranch == nil {
return nil, status.Errorf(codes.Internal, "git branch is nil")
}
var commit *rpc.Commit
var err error
if gitBranch.Commit != nil {
commit, err = mapGitCommit(gitBranch.Commit)
if err != nil {
return nil, err
}
}
return &rpc.Branch{
Name: gitBranch.Name,
Sha: gitBranch.SHA,
Commit: commit,
}, nil
}
func mapGitCommit(gitCommit *types.Commit) (*rpc.Commit, error) {
if gitCommit == nil {
return nil, status.Errorf(codes.Internal, "git commit is nil")
}
return &rpc.Commit{
Sha: gitCommit.Sha,
Sha: gitCommit.SHA,
Title: gitCommit.Title,
Message: gitCommit.Message,
Author: mapGitSignature(gitCommit.Author),

View File

@ -0,0 +1,22 @@
// 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 service
import (
"fmt"
"path/filepath"
)
// getFullPathForRepo returns the full path of a repo given the root dir of repos and the uid of the repo.
// NOTE: Split repos into subfolders using their prefix to distribute repos accross a set of folders.
func getFullPathForRepo(reposRoot, uid string) string {
// ASSUMPTION: repoUID is of lenth at least 4 - otherwise we have trouble either way.
return filepath.Join(
reposRoot, // root folder
uid[0:2], // first subfolder
uid[2:4], // second subfolder
fmt.Sprintf("%s.%s", uid[4:], gitRepoSuffix), // remainder with .git
)
}

View File

@ -10,8 +10,22 @@ import (
"strings"
"github.com/harness/gitness/gitrpc/internal/types"
"github.com/harness/gitness/gitrpc/rpc"
)
type ReferenceService struct {
rpc.UnimplementedReferenceServiceServer
adapter GitAdapter
reposRoot string
}
func NewReferenceService(adapter GitAdapter, reposRoot string) (*ReferenceService, error) {
return &ReferenceService{
adapter: adapter,
reposRoot: reposRoot,
}, nil
}
// sanitizeReferenceQuery removes characters that aren't allowd in a branch name.
// TODO: should we error out instead of ignore bad chars?
func sanitizeReferenceQuery(query string) (string, bool, bool) {

View File

@ -11,7 +11,6 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/harness/gitness/gitrpc/internal/types"
"github.com/harness/gitness/gitrpc/rpc"
@ -21,9 +20,8 @@ import (
)
const (
maxFileSize = 1 << 20
repoSubdirName = "repos"
gitRepoSuffix = "git"
maxFileSize = 1 << 20
gitRepoSuffix = "git"
gitReferenceNamePrefixBranch = "refs/heads/"
gitReferenceNamePrefixTag = "refs/tags/"
@ -48,14 +46,7 @@ type RepositoryService struct {
reposRoot string
}
func NewRepositoryService(adapter GitAdapter, store Storage, gitRoot string) (*RepositoryService, error) {
reposRoot := filepath.Join(gitRoot, repoSubdirName)
if _, err := os.Stat(reposRoot); errors.Is(err, os.ErrNotExist) {
if err = os.MkdirAll(reposRoot, 0o700); err != nil {
return nil, err
}
}
func NewRepositoryService(adapter GitAdapter, store Storage, reposRoot string) (*RepositoryService, error) {
return &RepositoryService{
adapter: adapter,
store: store,
@ -63,16 +54,6 @@ func NewRepositoryService(adapter GitAdapter, store Storage, gitRoot string) (*R
}, nil
}
func (s RepositoryService) getFullPathForRepo(uid string) string {
// split repos into subfolders using their prefix to distribute repos accross a set of folders.
return filepath.Join(
s.reposRoot, // root folder
uid[0:2], // first subfolder
uid[2:4], // second subfolder
fmt.Sprintf("%s.%s", uid[4:], gitRepoSuffix), // remainder with .git
)
}
//nolint:gocognit // need to refactor this code
func (s RepositoryService) CreateRepository(stream rpc.RepositoryService_CreateRepositoryServer) error {
// first get repo params from stream
@ -87,7 +68,7 @@ func (s RepositoryService) CreateRepository(stream rpc.RepositoryService_CreateR
}
log.Info().Msgf("received a create repository request %v", header)
repoPath := s.getFullPathForRepo(header.GetUid())
repoPath := getFullPathForRepo(s.reposRoot, header.GetUid())
if _, err = os.Stat(repoPath); !os.IsNotExist(err) {
return status.Errorf(codes.AlreadyExists, "repository exists already: %v", repoPath)
}
@ -101,13 +82,13 @@ func (s RepositoryService) CreateRepository(stream rpc.RepositoryService_CreateR
defer func(path string) {
_ = os.RemoveAll(path)
}(repoPath)
return fmt.Errorf("CreateRepository error: %w", err)
return processGitErrorf(err, "failed to initialize the repository")
}
// update default branch (currently set to non-existent branch)
err = s.adapter.SetDefaultBranch(ctx, repoPath, header.GetDefaultBranch(), true)
if err != nil {
return fmt.Errorf("error updating default branch for repo %s: %w", header.GetUid(), err)
return processGitErrorf(err, "error updating default branch for repo '%s'", header.GetUid())
}
// we need temp dir for cloning
@ -125,7 +106,7 @@ func (s RepositoryService) CreateRepository(stream rpc.RepositoryService_CreateR
// Clone repository to temp dir
if err = s.adapter.Clone(ctx, repoPath, tempDir, types.CloneRepoOptions{}); err != nil {
return status.Errorf(codes.Internal, "failed to clone repo: %v", err)
return processGitErrorf(err, "failed to clone repo")
}
// logic for receiving files

View File

@ -12,11 +12,11 @@ import (
func (s RepositoryService) GetSubmodule(ctx context.Context,
request *rpc.GetSubmoduleRequest) (*rpc.GetSubmoduleResponse, error) {
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
// TODO: do we need to validate request for nil?
gitSubmodule, err := s.adapter.GetSubmodule(ctx, repoPath, request.GetGitRef(), request.GetPath())
if err != nil {
return nil, err
return nil, processGitErrorf(err, "failed to get submodule")
}
return &rpc.GetSubmoduleResponse{

View File

@ -16,10 +16,10 @@ import (
)
//nolint:gocognit // need to refactor this code
func (s RepositoryService) ListCommitTags(request *rpc.ListCommitTagsRequest,
stream rpc.RepositoryService_ListCommitTagsServer) error {
func (s ReferenceService) ListCommitTags(request *rpc.ListCommitTagsRequest,
stream rpc.ReferenceService_ListCommitTagsServer) error {
ctx := stream.Context()
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
// get all required information from git references
tags, err := s.listCommitTagsLoadReferenceData(ctx, repoPath, request)
@ -43,7 +43,7 @@ func (s RepositoryService) ListCommitTags(request *rpc.ListCommitTagsRequest,
var gitTags []types.Tag
gitTags, err = s.adapter.GetAnnotatedTags(ctx, repoPath, annotatedTagSHAs)
if err != nil {
return status.Errorf(codes.Internal, "failed to get annotated tag: %v", err)
return processGitErrorf(err, "failed to get annotated tag")
}
ai := 0 // since only some tags are annotated, we need second index
@ -71,7 +71,7 @@ func (s RepositoryService) ListCommitTags(request *rpc.ListCommitTagsRequest,
var gitCommits []types.Commit
gitCommits, err = s.adapter.GetCommits(ctx, repoPath, commitSHAs)
if err != nil {
return status.Errorf(codes.Internal, "failed to get commits: %v", err)
return processGitErrorf(err, "failed to get commits")
}
for i := range gitCommits {
@ -118,7 +118,7 @@ var listCommitTagsRefFields = []types.GitReferenceField{types.GitReferenceFieldR
types.GitReferenceFieldObjectType, types.GitReferenceFieldObjectName}
var listCommitTagsObjectTypeFilter = []types.GitObjectType{types.GitObjectTypeCommit, types.GitObjectTypeTag}
func (s RepositoryService) listCommitTagsLoadReferenceData(ctx context.Context,
func (s ReferenceService) listCommitTagsLoadReferenceData(ctx context.Context,
repoPath string, request *rpc.ListCommitTagsRequest) ([]*rpc.CommitTag, error) {
// TODO: can we be smarter with slice allocation
tags := make([]*rpc.CommitTag, 0, 16)
@ -143,7 +143,7 @@ func (s RepositoryService) listCommitTagsLoadReferenceData(ctx context.Context,
err = s.adapter.WalkReferences(ctx, repoPath, handler, opts)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get tag references: %v", err)
return nil, processGitErrorf(err, "failed to walk tag references")
}
log.Trace().Msgf("git adapter returned %d tags", len(tags))

View File

@ -15,12 +15,12 @@ import (
func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
stream rpc.RepositoryService_ListTreeNodesServer) error {
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
gitNodes, err := s.adapter.ListTreeNodes(stream.Context(), repoPath,
request.GetGitRef(), request.GetPath(), request.GetRecursive(), request.GetIncludeLatestCommit())
if err != nil {
return status.Errorf(codes.Internal, "failed to list nodes: %v", err)
return processGitErrorf(err, "failed to list tree nodes")
}
log.Trace().Msgf("git adapter returned %d nodes", len(gitNodes))
@ -54,11 +54,11 @@ func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
func (s RepositoryService) GetTreeNode(ctx context.Context,
request *rpc.GetTreeNodeRequest) (*rpc.GetTreeNodeResponse, error) {
repoPath := s.getFullPathForRepo(request.GetRepoUid())
repoPath := getFullPathForRepo(s.reposRoot, request.GetRepoUid())
// TODO: do we need to validate request for nil?
gitNode, err := s.adapter.GetTreeNode(ctx, repoPath, request.GetGitRef(), request.GetPath())
if err != nil {
return nil, err
return nil, processGitErrorf(err, "failed to get tree node")
}
res := &rpc.GetTreeNodeResponse{
@ -76,7 +76,7 @@ func (s RepositoryService) GetTreeNode(ctx context.Context,
var commit *rpc.Commit
commit, err = s.getLatestCommit(ctx, repoPath, request.GetGitRef(), request.GetPath())
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get latest commit: %v", err)
return nil, err
}
res.Commit = commit
}

View File

@ -37,7 +37,7 @@ func (s RepositoryService) AddFilesAndPush(
err := s.adapter.AddFiles(repoPath, false, filePaths...)
if err != nil {
return err
return processGitErrorf(err, "failed to add files")
}
now := time.Now()
err = s.adapter.Commit(repoPath, types.CommitChangesOptions{
@ -60,7 +60,7 @@ func (s RepositoryService) AddFilesAndPush(
Message: message,
})
if err != nil {
return err
return processGitErrorf(err, "failed to commit files")
}
err = s.adapter.Push(ctx, repoPath, types.PushOptions{
// TODO: Don't hard-code
@ -72,7 +72,7 @@ func (s RepositoryService) AddFilesAndPush(
Timeout: 0,
})
if err != nil {
return err
return processGitErrorf(err, "failed to push files")
}
return nil

View File

@ -2,4 +2,12 @@
// 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 gitrpc
package types
import "errors"
var (
ErrAlreadyExists = errors.New("already exists")
ErrInvalidArgument = errors.New("invalid argument")
ErrNotFound = errors.New("not found")
)

View File

@ -126,13 +126,19 @@ type WalkReferencesOptions struct {
}
type Commit struct {
Sha string
SHA string
Title string
Message string
Author Signature
Committer Signature
}
type Branch struct {
Name string
SHA string
Commit *Commit
}
type Tag struct {
Sha string
Name string

View File

@ -9,8 +9,38 @@ import (
"time"
"github.com/harness/gitness/gitrpc/rpc"
"github.com/rs/zerolog/log"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Logs the error and message, returns either the provided message or a gitrpc equivalent if possible.
// Always logs the full message with error as warning.
func processRPCErrorf(err error, format string, args ...interface{}) error {
// create fallback error returned if we can't map it
fallbackErr := fmt.Errorf(format, args...)
// always log internal error together with message.
log.Warn().Msgf("%v: [RPC] %v", fallbackErr, err)
// ensure it's an rpc error
rpcErr, ok := status.FromError(err)
if !ok {
return fallbackErr
}
switch {
case rpcErr.Code() == codes.AlreadyExists:
return ErrAlreadyExists
case rpcErr.Code() == codes.NotFound:
return ErrNotFound
case rpcErr.Code() == codes.InvalidArgument:
return ErrInvalidArgument
default:
return fallbackErr
}
}
func mapToRPCSortOrder(o SortOrder) rpc.SortOrder {
switch o {
case SortOrderAsc:

88
gitrpc/proto/ref.proto Normal file
View File

@ -0,0 +1,88 @@
syntax = "proto3";
package rpc;
option go_package = "github.com/harness/gitness/gitrpc/rpc";
import "shared.proto";
service ReferenceService {
rpc CreateBranch(CreateBranchRequest) returns (CreateBranchResponse);
rpc DeleteBranch(DeleteBranchRequest) returns (DeleteBranchResponse);
rpc ListBranches(ListBranchesRequest) returns (stream ListBranchesResponse);
rpc ListCommitTags(ListCommitTagsRequest) returns (stream ListCommitTagsResponse);
}
message CreateBranchRequest {
string repo_uid = 1;
string branch_name = 2;
string target = 3;
}
message CreateBranchResponse {
Branch branch = 1;
}
message DeleteBranchRequest {
string repo_uid = 1;
string branch_name = 2;
bool force = 3;
}
message DeleteBranchResponse {
}
message ListBranchesRequest {
enum SortOption {
Default = 0;
Name = 1;
Date = 2;
}
string repo_uid = 1;
bool include_commit = 2;
string query = 3;
SortOption sort = 4;
SortOrder order = 5;
int32 page = 6;
int32 pageSize = 7;
}
message ListBranchesResponse {
Branch branch = 1;
}
message Branch {
string name = 1;
string sha = 2;
Commit commit = 3;
}
message ListCommitTagsRequest {
enum SortOption {
Default = 0;
Name = 1;
Date = 2;
}
string repo_uid = 1;
bool include_commit = 2;
string query = 3;
SortOption sort = 4;
SortOrder order = 5;
int32 page = 6;
int32 pageSize = 7;
}
message ListCommitTagsResponse {
CommitTag tag = 1;
}
message CommitTag {
string name = 1;
string sha = 2;
bool is_annotated = 3;
string title = 4;
string message = 5;
Signature tagger = 6;
Commit commit = 7;
}

View File

@ -13,8 +13,6 @@ service RepositoryService {
rpc GetSubmodule(GetSubmoduleRequest) returns (GetSubmoduleResponse);
rpc GetBlob(GetBlobRequest) returns (GetBlobResponse);
rpc ListCommits(ListCommitsRequest) returns (stream ListCommitsResponse);
rpc ListBranches(ListBranchesRequest) returns (stream ListBranchesResponse);
rpc ListCommitTags(ListCommitTagsRequest) returns (stream ListCommitTagsResponse);
}
message CreateRepositoryRequest {
@ -96,32 +94,6 @@ message ListCommitsResponseHeader {
int64 total_count = 1;
}
message ListBranchesRequest {
enum SortOption {
Default = 0;
Name = 1;
Date = 2;
}
string repo_uid = 1;
bool include_commit = 2;
string query = 3;
SortOption sort = 4;
SortOrder order = 5;
int32 page = 6;
int32 pageSize = 7;
}
message ListBranchesResponse {
Branch branch = 1;
}
message Branch {
string name = 1;
string sha = 2;
Commit commit = 3;
}
message GetBlobRequest {
string repo_uid = 1;
string sha = 2;
@ -151,52 +123,4 @@ message GetSubmoduleResponse {
message Submodule {
string name = 1;
string url = 2;
}
message ListCommitTagsRequest {
enum SortOption {
Default = 0;
Name = 1;
Date = 2;
}
string repo_uid = 1;
bool include_commit = 2;
string query = 3;
SortOption sort = 4;
SortOrder order = 5;
int32 page = 6;
int32 pageSize = 7;
}
message ListCommitTagsResponse {
CommitTag tag = 1;
}
message CommitTag {
string name = 1;
string sha = 2;
bool is_annotated = 3;
string title = 4;
string message = 5;
Signature tagger = 6;
Commit commit = 7;
}
message Commit {
string sha = 1;
string title = 2;
string message = 3;
Signature author = 4;
Signature committer = 5;
}
message Signature {
Identity identity = 1;
int64 when = 2;
}
message Identity {
string name = 1;
string email = 2;
}
}

View File

@ -23,4 +23,22 @@ enum SortOrder {
Default = 0;
Asc = 1;
Desc = 2;
}
}
message Commit {
string sha = 1;
string title = 2;
string message = 3;
Signature author = 4;
Signature committer = 5;
}
message Signature {
Identity identity = 1;
int64 when = 2;
}
message Identity {
string name = 1;
string email = 2;
}

View File

@ -79,7 +79,7 @@ func (c *Client) CreateRepository(ctx context.Context,
_, err = stream.CloseAndRecv()
if err != nil {
return nil, err
return nil, processRPCErrorf(err, "failed to create repo on server (uid: '%s')", uid)
}
log.Ctx(ctx).Info().Msgf("completed git repo setup.")

1090
gitrpc/rpc/ref.pb.go Normal file

File diff suppressed because it is too large Load Diff

268
gitrpc/rpc/ref_grpc.pb.go Normal file
View File

@ -0,0 +1,268 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.21.7
// source: ref.proto
package rpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// ReferenceServiceClient is the client API for ReferenceService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ReferenceServiceClient interface {
CreateBranch(ctx context.Context, in *CreateBranchRequest, opts ...grpc.CallOption) (*CreateBranchResponse, error)
DeleteBranch(ctx context.Context, in *DeleteBranchRequest, opts ...grpc.CallOption) (*DeleteBranchResponse, error)
ListBranches(ctx context.Context, in *ListBranchesRequest, opts ...grpc.CallOption) (ReferenceService_ListBranchesClient, error)
ListCommitTags(ctx context.Context, in *ListCommitTagsRequest, opts ...grpc.CallOption) (ReferenceService_ListCommitTagsClient, error)
}
type referenceServiceClient struct {
cc grpc.ClientConnInterface
}
func NewReferenceServiceClient(cc grpc.ClientConnInterface) ReferenceServiceClient {
return &referenceServiceClient{cc}
}
func (c *referenceServiceClient) CreateBranch(ctx context.Context, in *CreateBranchRequest, opts ...grpc.CallOption) (*CreateBranchResponse, error) {
out := new(CreateBranchResponse)
err := c.cc.Invoke(ctx, "/rpc.ReferenceService/CreateBranch", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *referenceServiceClient) DeleteBranch(ctx context.Context, in *DeleteBranchRequest, opts ...grpc.CallOption) (*DeleteBranchResponse, error) {
out := new(DeleteBranchResponse)
err := c.cc.Invoke(ctx, "/rpc.ReferenceService/DeleteBranch", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *referenceServiceClient) ListBranches(ctx context.Context, in *ListBranchesRequest, opts ...grpc.CallOption) (ReferenceService_ListBranchesClient, error) {
stream, err := c.cc.NewStream(ctx, &ReferenceService_ServiceDesc.Streams[0], "/rpc.ReferenceService/ListBranches", opts...)
if err != nil {
return nil, err
}
x := &referenceServiceListBranchesClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type ReferenceService_ListBranchesClient interface {
Recv() (*ListBranchesResponse, error)
grpc.ClientStream
}
type referenceServiceListBranchesClient struct {
grpc.ClientStream
}
func (x *referenceServiceListBranchesClient) Recv() (*ListBranchesResponse, error) {
m := new(ListBranchesResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *referenceServiceClient) ListCommitTags(ctx context.Context, in *ListCommitTagsRequest, opts ...grpc.CallOption) (ReferenceService_ListCommitTagsClient, error) {
stream, err := c.cc.NewStream(ctx, &ReferenceService_ServiceDesc.Streams[1], "/rpc.ReferenceService/ListCommitTags", opts...)
if err != nil {
return nil, err
}
x := &referenceServiceListCommitTagsClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type ReferenceService_ListCommitTagsClient interface {
Recv() (*ListCommitTagsResponse, error)
grpc.ClientStream
}
type referenceServiceListCommitTagsClient struct {
grpc.ClientStream
}
func (x *referenceServiceListCommitTagsClient) Recv() (*ListCommitTagsResponse, error) {
m := new(ListCommitTagsResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// ReferenceServiceServer is the server API for ReferenceService service.
// All implementations must embed UnimplementedReferenceServiceServer
// for forward compatibility
type ReferenceServiceServer interface {
CreateBranch(context.Context, *CreateBranchRequest) (*CreateBranchResponse, error)
DeleteBranch(context.Context, *DeleteBranchRequest) (*DeleteBranchResponse, error)
ListBranches(*ListBranchesRequest, ReferenceService_ListBranchesServer) error
ListCommitTags(*ListCommitTagsRequest, ReferenceService_ListCommitTagsServer) error
mustEmbedUnimplementedReferenceServiceServer()
}
// UnimplementedReferenceServiceServer must be embedded to have forward compatible implementations.
type UnimplementedReferenceServiceServer struct {
}
func (UnimplementedReferenceServiceServer) CreateBranch(context.Context, *CreateBranchRequest) (*CreateBranchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateBranch not implemented")
}
func (UnimplementedReferenceServiceServer) DeleteBranch(context.Context, *DeleteBranchRequest) (*DeleteBranchResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteBranch not implemented")
}
func (UnimplementedReferenceServiceServer) ListBranches(*ListBranchesRequest, ReferenceService_ListBranchesServer) error {
return status.Errorf(codes.Unimplemented, "method ListBranches not implemented")
}
func (UnimplementedReferenceServiceServer) ListCommitTags(*ListCommitTagsRequest, ReferenceService_ListCommitTagsServer) error {
return status.Errorf(codes.Unimplemented, "method ListCommitTags not implemented")
}
func (UnimplementedReferenceServiceServer) mustEmbedUnimplementedReferenceServiceServer() {}
// UnsafeReferenceServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ReferenceServiceServer will
// result in compilation errors.
type UnsafeReferenceServiceServer interface {
mustEmbedUnimplementedReferenceServiceServer()
}
func RegisterReferenceServiceServer(s grpc.ServiceRegistrar, srv ReferenceServiceServer) {
s.RegisterService(&ReferenceService_ServiceDesc, srv)
}
func _ReferenceService_CreateBranch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateBranchRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReferenceServiceServer).CreateBranch(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpc.ReferenceService/CreateBranch",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReferenceServiceServer).CreateBranch(ctx, req.(*CreateBranchRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ReferenceService_DeleteBranch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteBranchRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReferenceServiceServer).DeleteBranch(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpc.ReferenceService/DeleteBranch",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReferenceServiceServer).DeleteBranch(ctx, req.(*DeleteBranchRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ReferenceService_ListBranches_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ListBranchesRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(ReferenceServiceServer).ListBranches(m, &referenceServiceListBranchesServer{stream})
}
type ReferenceService_ListBranchesServer interface {
Send(*ListBranchesResponse) error
grpc.ServerStream
}
type referenceServiceListBranchesServer struct {
grpc.ServerStream
}
func (x *referenceServiceListBranchesServer) Send(m *ListBranchesResponse) error {
return x.ServerStream.SendMsg(m)
}
func _ReferenceService_ListCommitTags_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ListCommitTagsRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(ReferenceServiceServer).ListCommitTags(m, &referenceServiceListCommitTagsServer{stream})
}
type ReferenceService_ListCommitTagsServer interface {
Send(*ListCommitTagsResponse) error
grpc.ServerStream
}
type referenceServiceListCommitTagsServer struct {
grpc.ServerStream
}
func (x *referenceServiceListCommitTagsServer) Send(m *ListCommitTagsResponse) error {
return x.ServerStream.SendMsg(m)
}
// ReferenceService_ServiceDesc is the grpc.ServiceDesc for ReferenceService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ReferenceService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "rpc.ReferenceService",
HandlerType: (*ReferenceServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateBranch",
Handler: _ReferenceService_CreateBranch_Handler,
},
{
MethodName: "DeleteBranch",
Handler: _ReferenceService_DeleteBranch_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "ListBranches",
Handler: _ReferenceService_ListBranches_Handler,
ServerStreams: true,
},
{
StreamName: "ListCommitTags",
Handler: _ReferenceService_ListCommitTags_Handler,
ServerStreams: true,
},
},
Metadata: "ref.proto",
}

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,6 @@ type RepositoryServiceClient interface {
GetSubmodule(ctx context.Context, in *GetSubmoduleRequest, opts ...grpc.CallOption) (*GetSubmoduleResponse, error)
GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error)
ListCommits(ctx context.Context, in *ListCommitsRequest, opts ...grpc.CallOption) (RepositoryService_ListCommitsClient, error)
ListBranches(ctx context.Context, in *ListBranchesRequest, opts ...grpc.CallOption) (RepositoryService_ListBranchesClient, error)
ListCommitTags(ctx context.Context, in *ListCommitTagsRequest, opts ...grpc.CallOption) (RepositoryService_ListCommitTagsClient, error)
}
type repositoryServiceClient struct {
@ -165,70 +163,6 @@ func (x *repositoryServiceListCommitsClient) Recv() (*ListCommitsResponse, error
return m, nil
}
func (c *repositoryServiceClient) ListBranches(ctx context.Context, in *ListBranchesRequest, opts ...grpc.CallOption) (RepositoryService_ListBranchesClient, error) {
stream, err := c.cc.NewStream(ctx, &RepositoryService_ServiceDesc.Streams[3], "/rpc.RepositoryService/ListBranches", opts...)
if err != nil {
return nil, err
}
x := &repositoryServiceListBranchesClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type RepositoryService_ListBranchesClient interface {
Recv() (*ListBranchesResponse, error)
grpc.ClientStream
}
type repositoryServiceListBranchesClient struct {
grpc.ClientStream
}
func (x *repositoryServiceListBranchesClient) Recv() (*ListBranchesResponse, error) {
m := new(ListBranchesResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *repositoryServiceClient) ListCommitTags(ctx context.Context, in *ListCommitTagsRequest, opts ...grpc.CallOption) (RepositoryService_ListCommitTagsClient, error) {
stream, err := c.cc.NewStream(ctx, &RepositoryService_ServiceDesc.Streams[4], "/rpc.RepositoryService/ListCommitTags", opts...)
if err != nil {
return nil, err
}
x := &repositoryServiceListCommitTagsClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type RepositoryService_ListCommitTagsClient interface {
Recv() (*ListCommitTagsResponse, error)
grpc.ClientStream
}
type repositoryServiceListCommitTagsClient struct {
grpc.ClientStream
}
func (x *repositoryServiceListCommitTagsClient) Recv() (*ListCommitTagsResponse, error) {
m := new(ListCommitTagsResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// RepositoryServiceServer is the server API for RepositoryService service.
// All implementations must embed UnimplementedRepositoryServiceServer
// for forward compatibility
@ -239,8 +173,6 @@ type RepositoryServiceServer interface {
GetSubmodule(context.Context, *GetSubmoduleRequest) (*GetSubmoduleResponse, error)
GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error)
ListCommits(*ListCommitsRequest, RepositoryService_ListCommitsServer) error
ListBranches(*ListBranchesRequest, RepositoryService_ListBranchesServer) error
ListCommitTags(*ListCommitTagsRequest, RepositoryService_ListCommitTagsServer) error
mustEmbedUnimplementedRepositoryServiceServer()
}
@ -266,12 +198,6 @@ func (UnimplementedRepositoryServiceServer) GetBlob(context.Context, *GetBlobReq
func (UnimplementedRepositoryServiceServer) ListCommits(*ListCommitsRequest, RepositoryService_ListCommitsServer) error {
return status.Errorf(codes.Unimplemented, "method ListCommits not implemented")
}
func (UnimplementedRepositoryServiceServer) ListBranches(*ListBranchesRequest, RepositoryService_ListBranchesServer) error {
return status.Errorf(codes.Unimplemented, "method ListBranches not implemented")
}
func (UnimplementedRepositoryServiceServer) ListCommitTags(*ListCommitTagsRequest, RepositoryService_ListCommitTagsServer) error {
return status.Errorf(codes.Unimplemented, "method ListCommitTags not implemented")
}
func (UnimplementedRepositoryServiceServer) mustEmbedUnimplementedRepositoryServiceServer() {}
// UnsafeRepositoryServiceServer may be embedded to opt out of forward compatibility for this service.
@ -407,48 +333,6 @@ func (x *repositoryServiceListCommitsServer) Send(m *ListCommitsResponse) error
return x.ServerStream.SendMsg(m)
}
func _RepositoryService_ListBranches_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ListBranchesRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RepositoryServiceServer).ListBranches(m, &repositoryServiceListBranchesServer{stream})
}
type RepositoryService_ListBranchesServer interface {
Send(*ListBranchesResponse) error
grpc.ServerStream
}
type repositoryServiceListBranchesServer struct {
grpc.ServerStream
}
func (x *repositoryServiceListBranchesServer) Send(m *ListBranchesResponse) error {
return x.ServerStream.SendMsg(m)
}
func _RepositoryService_ListCommitTags_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ListCommitTagsRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RepositoryServiceServer).ListCommitTags(m, &repositoryServiceListCommitTagsServer{stream})
}
type RepositoryService_ListCommitTagsServer interface {
Send(*ListCommitTagsResponse) error
grpc.ServerStream
}
type repositoryServiceListCommitTagsServer struct {
grpc.ServerStream
}
func (x *repositoryServiceListCommitTagsServer) Send(m *ListCommitTagsResponse) error {
return x.ServerStream.SendMsg(m)
}
// RepositoryService_ServiceDesc is the grpc.ServiceDesc for RepositoryService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -485,16 +369,6 @@ var RepositoryService_ServiceDesc = grpc.ServiceDesc{
Handler: _RepositoryService_ListCommits_Handler,
ServerStreams: true,
},
{
StreamName: "ListBranches",
Handler: _RepositoryService_ListBranches_Handler,
ServerStreams: true,
},
{
StreamName: "ListCommitTags",
Handler: _RepositoryService_ListCommitTags_Handler,
ServerStreams: true,
},
},
Metadata: "repo.proto",
}

View File

@ -251,6 +251,195 @@ func (x *Chunk) GetData() []byte {
return nil
}
type Commit struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Sha string `protobuf:"bytes,1,opt,name=sha,proto3" json:"sha,omitempty"`
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
Author *Signature `protobuf:"bytes,4,opt,name=author,proto3" json:"author,omitempty"`
Committer *Signature `protobuf:"bytes,5,opt,name=committer,proto3" json:"committer,omitempty"`
}
func (x *Commit) Reset() {
*x = Commit{}
if protoimpl.UnsafeEnabled {
mi := &file_shared_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Commit) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Commit) ProtoMessage() {}
func (x *Commit) ProtoReflect() protoreflect.Message {
mi := &file_shared_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Commit.ProtoReflect.Descriptor instead.
func (*Commit) Descriptor() ([]byte, []int) {
return file_shared_proto_rawDescGZIP(), []int{3}
}
func (x *Commit) GetSha() string {
if x != nil {
return x.Sha
}
return ""
}
func (x *Commit) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *Commit) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *Commit) GetAuthor() *Signature {
if x != nil {
return x.Author
}
return nil
}
func (x *Commit) GetCommitter() *Signature {
if x != nil {
return x.Committer
}
return nil
}
type Signature struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Identity *Identity `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"`
When int64 `protobuf:"varint,2,opt,name=when,proto3" json:"when,omitempty"`
}
func (x *Signature) Reset() {
*x = Signature{}
if protoimpl.UnsafeEnabled {
mi := &file_shared_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Signature) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Signature) ProtoMessage() {}
func (x *Signature) ProtoReflect() protoreflect.Message {
mi := &file_shared_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Signature.ProtoReflect.Descriptor instead.
func (*Signature) Descriptor() ([]byte, []int) {
return file_shared_proto_rawDescGZIP(), []int{4}
}
func (x *Signature) GetIdentity() *Identity {
if x != nil {
return x.Identity
}
return nil
}
func (x *Signature) GetWhen() int64 {
if x != nil {
return x.When
}
return 0
}
type Identity struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
}
func (x *Identity) Reset() {
*x = Identity{}
if protoimpl.UnsafeEnabled {
mi := &file_shared_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Identity) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Identity) ProtoMessage() {}
func (x *Identity) ProtoReflect() protoreflect.Message {
mi := &file_shared_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Identity.ProtoReflect.Descriptor instead.
func (*Identity) Descriptor() ([]byte, []int) {
return file_shared_proto_rawDescGZIP(), []int{5}
}
func (x *Identity) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Identity) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
var File_shared_proto protoreflect.FileDescriptor
var file_shared_proto_rawDesc = []byte{
@ -267,13 +456,31 @@ var file_shared_proto_rawDesc = []byte{
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x2d, 0x0a, 0x05, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12,
0x10, 0x0a, 0x03, 0x65, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6f,
0x66, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x2b, 0x0a, 0x09, 0x53, 0x6f, 0x72, 0x74, 0x4f, 0x72, 0x64,
0x65, 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x10, 0x00, 0x12,
0x07, 0x0a, 0x03, 0x41, 0x73, 0x63, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x65, 0x73, 0x63,
0x10, 0x02, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x68, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73,
0x2f, 0x67, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xa0, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
0x12, 0x10, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73,
0x68, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
0x72, 0x65, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x6f,
0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x63,
0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x22, 0x4a, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x12, 0x12, 0x0a, 0x04, 0x77, 0x68, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04,
0x77, 0x68, 0x65, 0x6e, 0x22, 0x34, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2a, 0x2b, 0x0a, 0x09, 0x53, 0x6f,
0x72, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x73, 0x63, 0x10, 0x01, 0x12, 0x08, 0x0a,
0x04, 0x44, 0x65, 0x73, 0x63, 0x10, 0x02, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69,
0x74, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -289,21 +496,27 @@ func file_shared_proto_rawDescGZIP() []byte {
}
var file_shared_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_shared_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_shared_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_shared_proto_goTypes = []interface{}{
(SortOrder)(0), // 0: rpc.SortOrder
(*FileUpload)(nil), // 1: rpc.FileUpload
(*FileUploadHeader)(nil), // 2: rpc.FileUploadHeader
(*Chunk)(nil), // 3: rpc.Chunk
(*Commit)(nil), // 4: rpc.Commit
(*Signature)(nil), // 5: rpc.Signature
(*Identity)(nil), // 6: rpc.Identity
}
var file_shared_proto_depIdxs = []int32{
2, // 0: rpc.FileUpload.header:type_name -> rpc.FileUploadHeader
3, // 1: rpc.FileUpload.chunk:type_name -> rpc.Chunk
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
5, // 2: rpc.Commit.author:type_name -> rpc.Signature
5, // 3: rpc.Commit.committer:type_name -> rpc.Signature
6, // 4: rpc.Signature.identity:type_name -> rpc.Identity
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_shared_proto_init() }
@ -348,6 +561,42 @@ func file_shared_proto_init() {
return nil
}
}
file_shared_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Commit); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_shared_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Signature); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_shared_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Identity); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_shared_proto_msgTypes[0].OneofWrappers = []interface{}{
(*FileUpload_Header)(nil),
@ -359,7 +608,7 @@ func file_shared_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_shared_proto_rawDesc,
NumEnums: 1,
NumMessages: 3,
NumMessages: 6,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -5,6 +5,10 @@
package server
import (
"errors"
"os"
"path/filepath"
"github.com/harness/gitness/gitrpc/internal/gitea"
"github.com/harness/gitness/gitrpc/internal/service"
"github.com/harness/gitness/gitrpc/internal/storage"
@ -16,6 +20,10 @@ import (
"google.golang.org/grpc"
)
const (
repoSubdirName = "repos"
)
type Server struct {
*grpc.Server
Bind string
@ -23,6 +31,14 @@ type Server struct {
// TODO: this wiring should be done by wire.
func NewServer(bind string, gitRoot string) (*Server, error) {
// Create repos folder
reposRoot := filepath.Join(gitRoot, repoSubdirName)
if _, err := os.Stat(reposRoot); errors.Is(err, os.ErrNotExist) {
if err = os.MkdirAll(reposRoot, 0o700); err != nil {
return nil, err
}
}
// TODO: should be subdir of gitRoot? What is it being used for?
setting.Git.HomePath = "home"
adapter, err := gitea.New()
@ -32,16 +48,22 @@ func NewServer(bind string, gitRoot string) (*Server, error) {
s := grpc.NewServer()
store := storage.NewLocalStore()
// initialize services
repoService, err := service.NewRepositoryService(adapter, store, gitRoot)
repoService, err := service.NewRepositoryService(adapter, store, reposRoot)
if err != nil {
return nil, err
}
httpService, err := service.NewHTTPService(adapter, gitRoot)
// initialize services
refService, err := service.NewReferenceService(adapter, reposRoot)
if err != nil {
return nil, err
}
httpService, err := service.NewHTTPService(adapter, reposRoot)
if err != nil {
return nil, err
}
// register services
rpc.RegisterRepositoryServiceServer(s, repoService)
rpc.RegisterReferenceServiceServer(s, refService)
rpc.RegisterSmartHTTPServiceServer(s, httpService)
return &Server{

View File

@ -135,7 +135,7 @@ func (c *Client) ServicePack(ctx context.Context, w io.Writer, params *ServicePa
break
}
if err != nil {
return fmt.Errorf("PostUploadPack() error receiving stream bytes: %w", err)
return processRPCErrorf(err, "PostUploadPack() error receiving stream bytes")
}
if response.GetData() == nil {
return fmt.Errorf("PostUploadPack() data is nil")

View File

@ -37,7 +37,7 @@ func (c *Client) GetSubmodule(ctx context.Context, params *GetSubmoduleParams) (
Path: params.Path,
})
if err != nil {
return nil, err
return nil, processRPCErrorf(err, "failed to get submodule from server")
}
if resp.GetSubmodule() == nil {
return nil, fmt.Errorf("rpc submodule is nil")

View File

@ -52,7 +52,7 @@ func (c *Client) ListCommitTags(ctx context.Context, params *ListCommitTagsParam
return nil, ErrNoParamsProvided
}
stream, err := c.repoService.ListCommitTags(ctx, &rpc.ListCommitTagsRequest{
stream, err := c.refService.ListCommitTags(ctx, &rpc.ListCommitTagsRequest{
RepoUid: params.RepoUID,
IncludeCommit: params.IncludeCommit,
Query: params.Query,
@ -77,7 +77,7 @@ func (c *Client) ListCommitTags(ctx context.Context, params *ListCommitTagsParam
break
}
if err != nil {
return nil, fmt.Errorf("received unexpected error from rpc: %w", err)
return nil, processRPCErrorf(err, "received unexpected error from server")
}
if next.GetTag() == nil {
return nil, fmt.Errorf("expected tag message")
@ -92,7 +92,6 @@ func (c *Client) ListCommitTags(ctx context.Context, params *ListCommitTagsParam
output.Tags = append(output.Tags, *tag)
}
// TODO: is this needed?
err = stream.CloseSend()
if err != nil {
return nil, fmt.Errorf("failed to close stream")

View File

@ -88,7 +88,7 @@ func (c *Client) GetTreeNode(ctx context.Context, params *GetTreeNodeParams) (*G
IncludeLatestCommit: params.IncludeLatestCommit,
})
if err != nil {
return nil, err
return nil, processRPCErrorf(err, "failed to get tree node from server")
}
node, err := mapRPCTreeNode(resp.GetNode())
@ -133,7 +133,7 @@ func (c *Client) ListTreeNodes(ctx context.Context, params *ListTreeNodeParams)
break
}
if err != nil {
return nil, fmt.Errorf("received unexpected error from rpc: %w", err)
return nil, processRPCErrorf(err, "received unexpected error from server")
}
var node TreeNode

View File

@ -0,0 +1,79 @@
// 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 repo
import (
"context"
"fmt"
"github.com/harness/gitness/gitrpc"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
// CreateBranchInput used for branch creation apis.
type CreateBranchInput struct {
Name string `json:"name"`
// Target is the commit (or points to the commit) the new branch will be pointing to.
// If no target is provided, the branch points to the same commit as the default branch of the repo.
Target *string `json:"target"`
}
/*
* Creates a new branch for a repo.
*/
func (c *Controller) CreateBranch(ctx context.Context, session *auth.Session,
repoRef string, in *CreateBranchInput) (*Branch, error) {
repo, err := findRepoFromRef(ctx, c.repoStore, repoRef)
if err != nil {
return nil, err
}
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoEdit, false); err != nil {
return nil, err
}
// set target to default branch in case no target was provided
if in.Target == nil || *in.Target == "" {
in.Target = &repo.DefaultBranch
}
err = checkBranchName(in.Name)
if err != nil {
return nil, err
}
rpcOut, err := c.gitRPCClient.CreateBranch(ctx, &gitrpc.CreateBranchParams{
RepoUID: repo.GitUID,
BranchName: in.Name,
Target: *in.Target,
})
if err != nil {
return nil, err
}
branch, err := mapBranch(rpcOut.Branch)
if err != nil {
return nil, fmt.Errorf("failed to map branch: %w", err)
}
return &branch, nil
}
// checkBranchName does some basic branch validation
// We only ensure there are no control characters, the rest is up to git.
// TODO: Do we need some more validation here?
func checkBranchName(name string) error {
// fail fast on missing name
if len(name) == 0 {
return usererror.ErrBadRequest
}
return check.ForControlCharacters(name)
}

View File

@ -0,0 +1,47 @@
// 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 repo
import (
"context"
"github.com/harness/gitness/gitrpc"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types/enum"
)
/*
* DeletePath deletes a repo branch.
*/
func (c *Controller) DeleteBranch(ctx context.Context, session *auth.Session, repoRef string, branchName string) error {
repo, err := findRepoFromRef(ctx, c.repoStore, repoRef)
if err != nil {
return err
}
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoEdit, false); err != nil {
return err
}
// make sure user isn't deleting the default branch
// ASSUMPTION: lower layer calls explicit branch api
// and 'refs/heads/branch1' would fail if 'branch1' exists.
// TODO: Add functional test to ensure the scenario is covered!
if branchName == repo.DefaultBranch {
return usererror.ErrDefaultBranchCantBeDeleted
}
err = c.gitRPCClient.DeleteBranch(ctx, &gitrpc.DeleteBranchParams{
RepoUID: repo.GitUID,
BranchName: branchName,
})
if err != nil {
return err
}
return nil
}

View File

@ -13,10 +13,8 @@ import (
"github.com/harness/gitness/gitrpc"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
const (
@ -125,7 +123,6 @@ func (c *Controller) GetContent(ctx context.Context, session *auth.Session, repo
gitRef = repo.DefaultBranch
}
log := log.Ctx(ctx)
treeNodeOutput, err := c.gitRPCClient.GetTreeNode(ctx, &gitrpc.GetTreeNodeParams{
RepoUID: repo.GitUID,
GitREF: gitRef,
@ -133,11 +130,7 @@ func (c *Controller) GetContent(ctx context.Context, session *auth.Session, repo
IncludeLatestCommit: includeLatestCommit,
})
if err != nil {
// TODO: this should only return not found if it's an actual not found error.
// This requires gitrpc to also return notfound though!
log.Debug().Err(err).
Msgf("unable to find content for repo '%s', gitRef '%s' and path '%s'", repoRef, gitRef, repoPath)
return nil, usererror.ErrNotFound
return nil, err
}
info, err := mapToContentInfo(&treeNodeOutput.Node, treeNodeOutput.Commit)

View File

@ -0,0 +1,44 @@
// 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 repo
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/internal/api/controller/repo"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
)
/*
* Writes json-encoded branch information to the http response body.
*/
func HandleCreateBranch(repoCtrl *repo.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
repoRef, err := request.GetRepoRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
in := new(repo.CreateBranchInput)
err = json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(w, "Invalid request body: %s.", err)
return
}
branch, err := repoCtrl.CreateBranch(ctx, session, repoRef, in)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, branch)
}
}

View File

@ -0,0 +1,40 @@
// 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 repo
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/repo"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
)
/*
* Deletes a given branch.
*/
func HandleDeleteBranch(repoCtrl *repo.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
repoRef, err := request.GetRepoRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
branchName, err := request.GetRemainderFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
err = repoCtrl.DeleteBranch(ctx, session, repoRef, branchName)
if err != nil {
render.TranslatedUserError(w, err)
}
render.DeleteSuccessful(w)
}
}

View File

@ -27,6 +27,7 @@ func init() {
// TranslatedUserError writes the translated user error of the provided error.
func TranslatedUserError(w http.ResponseWriter, err error) {
log.Warn().Msgf("operation resulted in user facing error. Internal details: %s", err)
UserError(w, usererror.Translate(err))
}

View File

@ -111,6 +111,11 @@ func GetOptionalRemainderFromPath(r *http.Request) string {
return PathParamOrEmpty(r, PathParamRemainder)
}
// GetRemainderFromPath returns the remainder ("*") from the path or an an error if it doesn't exist.
func GetRemainderFromPath(r *http.Request) (string, error) {
return PathParamOrError(r, PathParamRemainder)
}
// ParseQuery extracts the query parameter from the url.
func ParseQuery(r *http.Request) string {
return r.FormValue(QueryParamQuery)

View File

@ -8,6 +8,7 @@ import (
"errors"
"net/http"
"github.com/harness/gitness/gitrpc"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/types/check"
@ -47,6 +48,14 @@ func Translate(err error) *Error {
case errors.Is(err, store.ErrSpaceWithChildsCantBeDeleted):
return ErrSpaceWithChildsCantBeDeleted
// gitrpc errors
case errors.Is(err, gitrpc.ErrAlreadyExists):
return ErrDuplicate
case errors.Is(err, gitrpc.ErrInvalidArgument):
return ErrBadRequest
case errors.Is(err, gitrpc.ErrNotFound):
return ErrNotFound
// unknown error
default:
log.Warn().Msgf("Unable to translate error: %s", err)

View File

@ -29,7 +29,7 @@ var (
ErrNoChange = New(http.StatusBadRequest, "No Change")
// ErrDuplicate is returned when a resource already exits.
ErrDuplicate = New(http.StatusBadRequest, "Resource already exists")
ErrDuplicate = New(http.StatusConflict, "Resource already exists")
// ErrPrimaryPathCantBeDeleted is returned when trying to delete a primary path.
ErrPrimaryPathCantBeDeleted = New(http.StatusBadRequest, "The primary path of an object can't be deleted")
@ -44,6 +44,9 @@ var (
// still has child resources.
ErrSpaceWithChildsCantBeDeleted = New(http.StatusBadRequest,
"Space can't be deleted as it still contains child resources")
// ErrDefaultBranchCantBeDeleted is returned if the user tries to delete the default branch of a repository.
ErrDefaultBranchCantBeDeleted = New(http.StatusBadRequest, "The default branch of a repository can't be deleted")
)
// Error represents a json-encoded API error.

View File

@ -168,6 +168,8 @@ func setupRepos(r chi.Router, repoCtrl *repo.Controller) {
// branch operations
r.Route("/branches", func(r chi.Router) {
r.Get("/", handlerrepo.HandleListBranches(repoCtrl))
r.Post("/", handlerrepo.HandleCreateBranch(repoCtrl))
r.Delete("/*", handlerrepo.HandleDeleteBranch(repoCtrl))
})
// tags operations

View File

@ -37,19 +37,24 @@ func offset(page, size int) int {
return page * size
}
// Logs the error and message, returns either the original error or a store equivalent if possible.
// Logs the error and message, returns either the provided message or a gitrpc equivalent if possible.
// Always logs the full message with error as warning.
func processSQLErrorf(err error, format string, args ...interface{}) error {
// always log DB error (print formated message)
log.Debug().Msgf("%s %s", fmt.Sprintf(format, args...), err)
// create fallback error returned if we can't map it
fallbackErr := fmt.Errorf(format, args...)
// always log internal error together with message.
log.Debug().Msgf("%v: [SQL] %v", fallbackErr, err)
// If it's a known error, return converted error instead.
if errors.Is(err, sql.ErrNoRows) {
switch {
case errors.Is(err, sql.ErrNoRows):
return store.ErrResourceNotFound
} else if isSQLUniqueConstraintError(err) {
case isSQLUniqueConstraintError(err):
return store.ErrDuplicate
default:
return fallbackErr
}
return err
}
func isSQLUniqueConstraintError(original error) bool {

View File

@ -7,7 +7,6 @@ package check
import (
"fmt"
"regexp"
"strings"
)
const (
@ -26,7 +25,6 @@ var (
ErrDisplayNameLength = &ValidationError{
fmt.Sprintf("DisplayName has to be between %d and %d in length.", minDisplayNameLength, maxDisplayNameLength),
}
ErrDisplayNameContainsInvalidASCII = &ValidationError{"DisplayName has to consist of valid ASCII characters."}
ErrUIDLength = &ValidationError{
fmt.Sprintf("UID has to be between %d and %d in length.",
@ -39,6 +37,8 @@ var (
ErrEmailLen = &ValidationError{
fmt.Sprintf("Email address has to be within %d and %d characters", minEmailLength, maxEmailLength),
}
ErrInvalidCharacters = &ValidationError{"Input contains invalid characters."}
)
// DisplayName checks the provided display name and returns an error if it isn't valid.
@ -48,16 +48,15 @@ func DisplayName(displayName string) error {
return ErrDisplayNameLength
}
// created sanitized string restricted to ASCII characters (without control characters).
sanitizedString := strings.Map(func(r rune) rune {
if r < 32 || r == 127 || r > 255 {
return -1
}
return r
}, displayName)
return ForControlCharacters(displayName)
}
if len(sanitizedString) != len(displayName) {
return ErrDisplayNameContainsInvalidASCII
// ForControlCharacters ensures that there are no control characters in the provided string.
func ForControlCharacters(s string) error {
for _, r := range s {
if r < 32 || r == 127 {
return ErrInvalidCharacters
}
}
return nil