mirror of https://github.com/harness/drone.git
Add repo summary endpoint (#2037)
parent
c365ef246a
commit
adf041d747
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/git"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// Summary returns commit, branch, tag and pull req count for a repo.
|
||||
func (c *Controller) Summary(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
) (*types.RepositorySummary, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("access check failed: %w", err)
|
||||
}
|
||||
|
||||
summary, err := c.git.Summary(ctx, &git.ReadParams{RepoUID: repo.GitUID})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get repo summary: %w", err)
|
||||
}
|
||||
|
||||
return &types.RepositorySummary{
|
||||
DefaultBranchCommitCount: summary.CommitCount,
|
||||
BranchCount: summary.BranchCount,
|
||||
TagCount: summary.TagCount,
|
||||
PullReqSummary: types.RepositoryPullReqSummary{
|
||||
OpenCount: repo.NumOpenPulls,
|
||||
ClosedCount: repo.NumClosedPulls,
|
||||
MergedCount: repo.NumMergedPulls,
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
// HandleSummary writes json-encoded repository summary information to the http response body.
|
||||
func HandleSummary(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(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
summary, err := repoCtrl.Summary(ctx, session, repoRef)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, summary)
|
||||
}
|
||||
}
|
|
@ -1154,4 +1154,17 @@ func repoOperations(reflector *openapi3.Reflector) {
|
|||
_ = reflector.SetJSONResponse(&opArchive, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opArchive, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/archive/{git_ref}.{format}", opArchive)
|
||||
|
||||
opSummary := openapi3.Operation{}
|
||||
opSummary.WithTags("repository")
|
||||
opSummary.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "summary"})
|
||||
_ = reflector.SetRequest(&opSummary, new(repoRequest), http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(types.RepositorySummary), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/summary", opSummary)
|
||||
}
|
||||
|
|
|
@ -287,6 +287,8 @@ func setupRepos(r chi.Router,
|
|||
r.Patch("/general", handlerreposettings.HandleGeneralUpdate(repoSettingsCtrl))
|
||||
})
|
||||
|
||||
r.Get("/summary", handlerrepo.HandleSummary(repoCtrl))
|
||||
|
||||
r.Post("/move", handlerrepo.HandleMove(repoCtrl))
|
||||
r.Get("/service-accounts", handlerrepo.HandleListServiceAccounts(repoCtrl))
|
||||
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/git/command"
|
||||
|
@ -104,3 +106,42 @@ func (g *Git) IsBranchExist(ctx context.Context, repoPath, name string) (bool, e
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (g *Git) GetBranchCount(
|
||||
ctx context.Context,
|
||||
repoPath string,
|
||||
) (int, error) {
|
||||
if repoPath == "" {
|
||||
return 0, ErrRepositoryPathEmpty
|
||||
}
|
||||
|
||||
pipeOut, pipeIn := io.Pipe()
|
||||
defer pipeOut.Close()
|
||||
|
||||
cmd := command.New("branch",
|
||||
command.WithFlag("--list"),
|
||||
command.WithFlag("--format=%(refname:short)"),
|
||||
)
|
||||
|
||||
var err error
|
||||
go func() {
|
||||
defer pipeIn.Close()
|
||||
err = cmd.Run(ctx, command.WithDir(repoPath), command.WithStdout(pipeIn))
|
||||
}()
|
||||
if err != nil {
|
||||
return 0, processGitErrorf(err, "failed to trigger branch command")
|
||||
}
|
||||
|
||||
return countLines(pipeOut), nil
|
||||
}
|
||||
|
||||
func countLines(pipe io.Reader) int {
|
||||
scanner := bufio.NewScanner(pipe)
|
||||
count := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
count++
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
|
|
@ -398,3 +398,28 @@ l:
|
|||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
func (g *Git) GetTagCount(
|
||||
ctx context.Context,
|
||||
repoPath string,
|
||||
) (int, error) {
|
||||
if repoPath == "" {
|
||||
return 0, ErrRepositoryPathEmpty
|
||||
}
|
||||
|
||||
pipeOut, pipeIn := io.Pipe()
|
||||
defer pipeOut.Close()
|
||||
|
||||
cmd := command.New("tag")
|
||||
|
||||
var err error
|
||||
go func() {
|
||||
defer pipeIn.Close()
|
||||
err = cmd.Run(ctx, command.WithDir(repoPath), command.WithStdout(pipeIn))
|
||||
}()
|
||||
if err != nil {
|
||||
return 0, processGitErrorf(err, "failed to trigger branch command")
|
||||
}
|
||||
|
||||
return countLines(pipeOut), nil
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ var descriptions = map[string]builder{
|
|||
// git-blame(1) does not support disambiguating options from paths from revisions.
|
||||
flags: NoRefUpdates | NoEndOfOptions,
|
||||
},
|
||||
"branch": {},
|
||||
"bundle": {
|
||||
flags: NoRefUpdates,
|
||||
},
|
||||
|
|
|
@ -102,4 +102,9 @@ type Interface interface {
|
|||
*/
|
||||
ScanSecrets(ctx context.Context, param *ScanSecretsParams) (*ScanSecretsOutput, error)
|
||||
Archive(ctx context.Context, params ArchiveParams, w io.Writer) error
|
||||
|
||||
/*
|
||||
* Repo Summary service
|
||||
*/
|
||||
Summary(ctx context.Context, params *ReadParams) (*SummaryOutput, error)
|
||||
}
|
||||
|
|
|
@ -89,7 +89,11 @@ func CommitCount(
|
|||
repoPath string,
|
||||
start, end string,
|
||||
) (int, error) {
|
||||
cmd := command.New("rev-list", command.WithFlag("--count"), command.WithArg(start+".."+end))
|
||||
arg := command.WithArg(end)
|
||||
if len(start) > 0 {
|
||||
arg = command.WithArg(start + ".." + end)
|
||||
}
|
||||
cmd := command.New("rev-list", command.WithFlag("--count"), arg)
|
||||
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/git/merge"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type SummaryOutput struct {
|
||||
CommitCount int
|
||||
BranchCount int
|
||||
TagCount int
|
||||
}
|
||||
|
||||
func (s *Service) Summary(
|
||||
ctx context.Context,
|
||||
params *ReadParams,
|
||||
) (*SummaryOutput, error) {
|
||||
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
|
||||
|
||||
defaultBranch, err := s.git.GetDefaultBranch(ctx, repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultBranch = strings.TrimSpace(defaultBranch)
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var commitCount, branchCount, tagCount int
|
||||
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
commitCount, err = merge.CommitCount(ctx, repoPath, "", defaultBranch)
|
||||
return err
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
branchCount, err = s.git.GetBranchCount(ctx, repoPath)
|
||||
return err
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
tagCount, err = s.git.GetTagCount(ctx, repoPath)
|
||||
return err
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, fmt.Errorf("failed to get repo summary: %w", err)
|
||||
}
|
||||
|
||||
return &SummaryOutput{
|
||||
CommitCount: commitCount,
|
||||
BranchCount: branchCount,
|
||||
TagCount: tagCount,
|
||||
}, nil
|
||||
}
|
|
@ -98,3 +98,16 @@ type RepositoryGitInfo struct {
|
|||
ParentID int64
|
||||
GitUID string
|
||||
}
|
||||
|
||||
type RepositoryPullReqSummary struct {
|
||||
OpenCount int `json:"open_count"`
|
||||
ClosedCount int `json:"closed_count"`
|
||||
MergedCount int `json:"merged_count"`
|
||||
}
|
||||
|
||||
type RepositorySummary struct {
|
||||
DefaultBranchCommitCount int `json:"default_branch_commit_count"`
|
||||
BranchCount int `json:"branch_count"`
|
||||
TagCount int `json:"tag_count"`
|
||||
PullReqSummary RepositoryPullReqSummary `json:"pull_req_summary"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue