Add GitHub support
This commit is contained in:
parent
4b2b251136
commit
9f66001a66
@ -11,6 +11,7 @@ import (
|
|||||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config"
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config"
|
||||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes"
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes"
|
||||||
gitearemote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/gitea"
|
gitearemote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/gitea"
|
||||||
|
githubremote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/github"
|
||||||
gitlabremote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/gitlab"
|
gitlabremote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/gitlab"
|
||||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/remote"
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/remote"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@ -65,6 +66,8 @@ func getRemotes(cmd *cobra.Command) *remotes.Remotes {
|
|||||||
gitRemote, err = gitlabremote.NewGitlabRemote(&gitRemoteInfo)
|
gitRemote, err = gitlabremote.NewGitlabRemote(&gitRemoteInfo)
|
||||||
case "gitea":
|
case "gitea":
|
||||||
gitRemote, err = gitearemote.NewGiteaRemote(&gitRemoteInfo)
|
gitRemote, err = gitearemote.NewGiteaRemote(&gitRemoteInfo)
|
||||||
|
case "github":
|
||||||
|
gitRemote, err = githubremote.NewGithubRemote(&gitRemoteInfo)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Error("Failed to prepare remote", plog.Args(
|
plog.Error("Failed to prepare remote", plog.Args(
|
||||||
|
1
go.mod
1
go.mod
@ -37,6 +37,7 @@ require (
|
|||||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/google/go-github/v58 v58.0.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/gookit/color v1.5.4 // indirect
|
github.com/gookit/color v1.5.4 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -170,6 +170,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw=
|
||||||
|
github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
45
internal/remotes/github/github.go
Normal file
45
internal/remotes/github/github.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package githubremote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v58/github"
|
||||||
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/info"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GithubRemote struct {
|
||||||
|
user *github.User
|
||||||
|
info *info.RemoteInfo
|
||||||
|
api *github.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GithubRemote) GetInfo() *info.RemoteInfo {
|
||||||
|
return r.info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GithubRemote) GetType() string {
|
||||||
|
return r.info.Type.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GithubRemote) String() string {
|
||||||
|
return fmt.Sprintf("Github %s (%s), clone proto %s",
|
||||||
|
r.GetInfo().Name, r.GetInfo().Host, r.GetInfo().CloneProto)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGithubRemote(remoteInfo *info.RemoteInfo) (*GithubRemote, error) {
|
||||||
|
client := github.NewClient(nil).
|
||||||
|
WithAuthToken(remoteInfo.Token)
|
||||||
|
|
||||||
|
githubRemote := &GithubRemote{
|
||||||
|
info: remoteInfo,
|
||||||
|
api: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
user, _, err := githubRemote.api.Users.Get(remoteInfo.Context(), "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
githubRemote.user = user
|
||||||
|
|
||||||
|
return githubRemote, nil
|
||||||
|
}
|
72
internal/remotes/github/github_api.go
Normal file
72
internal/remotes/github/github_api.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package githubremote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v58/github"
|
||||||
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects"
|
||||||
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *GithubRemote) GetNumProjects(opts *remote.RemoteQueryOpts) int {
|
||||||
|
var projects int
|
||||||
|
if opts.OwnerOnly {
|
||||||
|
projects = int(r.user.GetOwnedPrivateRepos())
|
||||||
|
} else {
|
||||||
|
projects += int(r.user.GetTotalPrivateRepos())
|
||||||
|
projects += r.user.GetPublicRepos()
|
||||||
|
}
|
||||||
|
return projects
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GithubRemote) ReposToProjects(repos []*github.Repository) []*projects.Project {
|
||||||
|
pList := make([]*projects.Project, len(repos))
|
||||||
|
for i, repo := range repos {
|
||||||
|
var ownerName, avatar string
|
||||||
|
owner := repo.GetOwner()
|
||||||
|
if owner != nil {
|
||||||
|
ownerName = owner.GetName()
|
||||||
|
avatar = owner.GetAvatarURL()
|
||||||
|
}
|
||||||
|
// owner, name := GetOwnerRepo(repo.FullName)
|
||||||
|
project := &projects.Project{
|
||||||
|
ID: int(repo.GetID()),
|
||||||
|
Owner: ownerName,
|
||||||
|
Description: repo.GetDescription(),
|
||||||
|
SSHURLToRepo: repo.GetSSHURL(),
|
||||||
|
HTTPURLToRepo: repo.GetCloneURL(),
|
||||||
|
WebURL: repo.GetHTMLURL(),
|
||||||
|
Name: repo.GetName(),
|
||||||
|
NameWithNamespace: repo.GetFullName(),
|
||||||
|
Path: repo.GetName(),
|
||||||
|
AvatarURL: avatar,
|
||||||
|
PathWithNamespace: repo.GetFullName(),
|
||||||
|
LastActivityAt: repo.GetPushedAt().Time,
|
||||||
|
Remote: r.info.Host,
|
||||||
|
Languages: r.GetRepoLangs(repo),
|
||||||
|
}
|
||||||
|
pList[i] = project
|
||||||
|
}
|
||||||
|
return pList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GithubRemote) GetRepoLangs(repo *github.Repository) *projects.ProjectLanguages {
|
||||||
|
languages := projects.NewProjectLanguages()
|
||||||
|
langs, _, err := r.api.Repositories.ListLanguages(r.info.Context(), r.user.GetName(), repo.GetName())
|
||||||
|
if err != nil {
|
||||||
|
var ttlLines int
|
||||||
|
for _, lines := range langs {
|
||||||
|
ttlLines += lines
|
||||||
|
}
|
||||||
|
|
||||||
|
for l, n := range langs {
|
||||||
|
pcnt := float64(n) / float64(ttlLines) * 100
|
||||||
|
pcnt = math.Round(pcnt*100) / 100
|
||||||
|
languages.AddLanguage(&projects.ProjectLanguage{
|
||||||
|
Name: l,
|
||||||
|
Percentage: float32(pcnt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return languages
|
||||||
|
}
|
62
internal/remotes/github/github_stream.go
Normal file
62
internal/remotes/github/github_stream.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package githubremote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v58/github"
|
||||||
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/load"
|
||||||
|
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
const githubReposPerPage = 20
|
||||||
|
|
||||||
|
func (r *GithubRemote) StreamProjects(pi *load.ProgressInfo, opts *remote.RemoteQueryOpts) {
|
||||||
|
defer close(pi.ProgressChan)
|
||||||
|
defer close(pi.ProjectsChan)
|
||||||
|
|
||||||
|
// Get projects. TODO support concurrency
|
||||||
|
githubListOpts := github.ListOptions{
|
||||||
|
Page: 1,
|
||||||
|
PerPage: githubReposPerPage,
|
||||||
|
}
|
||||||
|
var githubRepoType string
|
||||||
|
if opts.OwnerOnly {
|
||||||
|
githubRepoType = "owner"
|
||||||
|
} else {
|
||||||
|
githubRepoType = "all"
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
repos, resp, err := r.api.Repositories.ListByAuthenticatedUser(
|
||||||
|
r.info.Context(),
|
||||||
|
&github.RepositoryListByAuthenticatedUserOptions{
|
||||||
|
Type: githubRepoType,
|
||||||
|
ListOptions: githubListOpts,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
pi.ErrorChan <- err
|
||||||
|
break
|
||||||
|
} else if len(repos) < 1 {
|
||||||
|
pi.ErrorChan <- errors.New("No github repos found")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write updates to channels
|
||||||
|
pi.ProjectsChan <- r.ReposToProjects(repos)
|
||||||
|
pi.ProgressChan <- load.Progress{
|
||||||
|
Page: githubListOpts.Page,
|
||||||
|
Pages: resp.LastPage,
|
||||||
|
Projects: len(repos),
|
||||||
|
TotalProjects: pi.NumProjects,
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.NextPage == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
githubListOpts.Page = resp.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
pi.DoneChan <- nil
|
||||||
|
}
|
@ -20,9 +20,11 @@ type RemoteTypes []RemoteType
|
|||||||
var (
|
var (
|
||||||
RemoteTypeGitlab RemoteType = "gitlab"
|
RemoteTypeGitlab RemoteType = "gitlab"
|
||||||
RemoteTypeGitea RemoteType = "gitea"
|
RemoteTypeGitea RemoteType = "gitea"
|
||||||
|
RemoteTypeGithub RemoteType = "github"
|
||||||
RemoteTypesAll RemoteTypes = []RemoteType{
|
RemoteTypesAll RemoteTypes = []RemoteType{
|
||||||
RemoteTypeGitea,
|
RemoteTypeGitea,
|
||||||
RemoteTypeGitlab,
|
RemoteTypeGitlab,
|
||||||
|
RemoteTypeGithub,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,6 +35,8 @@ func GetRemoteTypeFromString(remoteType string) RemoteType {
|
|||||||
rt = RemoteTypeGitea
|
rt = RemoteTypeGitea
|
||||||
case RemoteTypeGitlab.String():
|
case RemoteTypeGitlab.String():
|
||||||
rt = RemoteTypeGitlab
|
rt = RemoteTypeGitlab
|
||||||
|
case RemoteTypeGithub.String():
|
||||||
|
rt = RemoteTypeGithub
|
||||||
}
|
}
|
||||||
return rt
|
return rt
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user