123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- // Copyright 2019 The Gitea Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package migrations
-
- import (
- "context"
- "fmt"
- "net/http"
- "net/url"
- "strings"
- "time"
-
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/proxy"
- "code.gitea.io/gitea/modules/structs"
-
- "github.com/gogs/go-gogs-client"
- )
-
- var (
- _ base.Downloader = &GogsDownloader{}
- _ base.DownloaderFactory = &GogsDownloaderFactory{}
- )
-
- func init() {
- RegisterDownloaderFactory(&GogsDownloaderFactory{})
- }
-
- // GogsDownloaderFactory defines a gogs downloader factory
- type GogsDownloaderFactory struct {
- }
-
- // New returns a Downloader related to this factory according MigrateOptions
- func (f *GogsDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
- u, err := url.Parse(opts.CloneAddr)
- if err != nil {
- return nil, err
- }
-
- baseURL := u.Scheme + "://" + u.Host
- repoNameSpace := strings.TrimSuffix(u.Path, ".git")
- repoNameSpace = strings.Trim(repoNameSpace, "/")
-
- fields := strings.Split(repoNameSpace, "/")
- if len(fields) < 2 {
- return nil, fmt.Errorf("invalid path: %s", repoNameSpace)
- }
-
- log.Trace("Create gogs downloader. BaseURL: %s RepoOwner: %s RepoName: %s", baseURL, fields[0], fields[1])
- return NewGogsDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, fields[0], fields[1]), nil
- }
-
- // GitServiceType returns the type of git service
- func (f *GogsDownloaderFactory) GitServiceType() structs.GitServiceType {
- return structs.GogsService
- }
-
- // GogsDownloader implements a Downloader interface to get repository information
- // from gogs via API
- type GogsDownloader struct {
- base.NullDownloader
- ctx context.Context
- client *gogs.Client
- baseURL string
- repoOwner string
- repoName string
- userName string
- password string
- openIssuesFinished bool
- openIssuesPages int
- transport http.RoundTripper
- }
-
- // SetContext set context
- func (g *GogsDownloader) SetContext(ctx context.Context) {
- g.ctx = ctx
- }
-
- // NewGogsDownloader creates a gogs Downloader via gogs API
- func NewGogsDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GogsDownloader {
- var downloader = GogsDownloader{
- ctx: ctx,
- baseURL: baseURL,
- userName: userName,
- password: password,
- repoOwner: repoOwner,
- repoName: repoName,
- }
-
- var client *gogs.Client
- if len(token) != 0 {
- client = gogs.NewClient(baseURL, token)
- downloader.userName = token
- } else {
- var transport = NewMigrationHTTPTransport()
- transport.Proxy = func(req *http.Request) (*url.URL, error) {
- req.SetBasicAuth(userName, password)
- return proxy.Proxy()(req)
- }
- downloader.transport = transport
-
- client = gogs.NewClient(baseURL, "")
- client.SetHTTPClient(&http.Client{
- Transport: &downloader,
- })
- }
-
- downloader.client = client
- return &downloader
- }
-
- // RoundTrip wraps the provided request within this downloader's context and passes it to our internal http.Transport.
- // This implements http.RoundTripper and makes the gogs client requests cancellable even though it is not cancellable itself
- func (g *GogsDownloader) RoundTrip(req *http.Request) (*http.Response, error) {
- return g.transport.RoundTrip(req.WithContext(g.ctx))
- }
-
- // GetRepoInfo returns a repository information
- func (g *GogsDownloader) GetRepoInfo() (*base.Repository, error) {
- gr, err := g.client.GetRepo(g.repoOwner, g.repoName)
- if err != nil {
- return nil, err
- }
-
- // convert gogs repo to stand Repo
- return &base.Repository{
- Owner: g.repoOwner,
- Name: g.repoName,
- IsPrivate: gr.Private,
- Description: gr.Description,
- CloneURL: gr.CloneURL,
- OriginalURL: gr.HTMLURL,
- DefaultBranch: gr.DefaultBranch,
- }, nil
- }
-
- // GetMilestones returns milestones
- func (g *GogsDownloader) GetMilestones() ([]*base.Milestone, error) {
- var perPage = 100
- var milestones = make([]*base.Milestone, 0, perPage)
-
- ms, err := g.client.ListRepoMilestones(g.repoOwner, g.repoName)
- if err != nil {
- return nil, err
- }
-
- for _, m := range ms {
- milestones = append(milestones, &base.Milestone{
- Title: m.Title,
- Description: m.Description,
- Deadline: m.Deadline,
- State: string(m.State),
- Closed: m.Closed,
- })
- }
-
- return milestones, nil
- }
-
- // GetLabels returns labels
- func (g *GogsDownloader) GetLabels() ([]*base.Label, error) {
- var perPage = 100
- var labels = make([]*base.Label, 0, perPage)
- ls, err := g.client.ListRepoLabels(g.repoOwner, g.repoName)
- if err != nil {
- return nil, err
- }
-
- for _, label := range ls {
- labels = append(labels, convertGogsLabel(label))
- }
-
- return labels, nil
- }
-
- // GetIssues returns issues according start and limit, perPage is not supported
- func (g *GogsDownloader) GetIssues(page, _ int) ([]*base.Issue, bool, error) {
- var state string
- if g.openIssuesFinished {
- state = string(gogs.STATE_CLOSED)
- page -= g.openIssuesPages
- } else {
- state = string(gogs.STATE_OPEN)
- g.openIssuesPages = page
- }
-
- issues, isEnd, err := g.getIssues(page, state)
- if err != nil {
- return nil, false, err
- }
-
- if isEnd {
- if g.openIssuesFinished {
- return issues, true, nil
- }
- g.openIssuesFinished = true
- }
-
- return issues, false, nil
- }
-
- func (g *GogsDownloader) getIssues(page int, state string) ([]*base.Issue, bool, error) {
- var allIssues = make([]*base.Issue, 0, 10)
-
- issues, err := g.client.ListRepoIssues(g.repoOwner, g.repoName, gogs.ListIssueOption{
- Page: page,
- State: state,
- })
- if err != nil {
- return nil, false, fmt.Errorf("error while listing repos: %v", err)
- }
-
- for _, issue := range issues {
- if issue.PullRequest != nil {
- continue
- }
- allIssues = append(allIssues, convertGogsIssue(issue))
- }
-
- return allIssues, len(issues) == 0, nil
- }
-
- // GetComments returns comments according issueNumber
- func (g *GogsDownloader) GetComments(opts base.GetCommentOptions) ([]*base.Comment, bool, error) {
- var allComments = make([]*base.Comment, 0, 100)
-
- comments, err := g.client.ListIssueComments(g.repoOwner, g.repoName, opts.Context.ForeignID())
- if err != nil {
- return nil, false, fmt.Errorf("error while listing repos: %v", err)
- }
- for _, comment := range comments {
- if len(comment.Body) == 0 || comment.Poster == nil {
- continue
- }
- allComments = append(allComments, &base.Comment{
- IssueIndex: opts.Context.LocalID(),
- PosterID: comment.Poster.ID,
- PosterName: comment.Poster.Login,
- PosterEmail: comment.Poster.Email,
- Content: comment.Body,
- Created: comment.Created,
- Updated: comment.Updated,
- })
- }
-
- return allComments, true, nil
- }
-
- // GetTopics return repository topics
- func (g *GogsDownloader) GetTopics() ([]string, error) {
- return []string{}, nil
- }
-
- // FormatCloneURL add authentification into remote URLs
- func (g *GogsDownloader) FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error) {
- if len(opts.AuthToken) > 0 || len(opts.AuthUsername) > 0 {
- u, err := url.Parse(remoteAddr)
- if err != nil {
- return "", err
- }
- if len(opts.AuthToken) != 0 {
- u.User = url.UserPassword(opts.AuthToken, "")
- } else {
- u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword)
- }
- return u.String(), nil
- }
- return remoteAddr, nil
- }
-
- func convertGogsIssue(issue *gogs.Issue) *base.Issue {
- var milestone string
- if issue.Milestone != nil {
- milestone = issue.Milestone.Title
- }
- var labels = make([]*base.Label, 0, len(issue.Labels))
- for _, l := range issue.Labels {
- labels = append(labels, convertGogsLabel(l))
- }
-
- var closed *time.Time
- if issue.State == gogs.STATE_CLOSED {
- // gogs client haven't provide closed, so we use updated instead
- closed = &issue.Updated
- }
-
- return &base.Issue{
- Title: issue.Title,
- Number: issue.Index,
- PosterID: issue.Poster.ID,
- PosterName: issue.Poster.Login,
- PosterEmail: issue.Poster.Email,
- Content: issue.Body,
- Milestone: milestone,
- State: string(issue.State),
- Created: issue.Created,
- Updated: issue.Updated,
- Labels: labels,
- Closed: closed,
- Context: base.BasicIssueContext(issue.Index),
- }
- }
-
- func convertGogsLabel(label *gogs.Label) *base.Label {
- return &base.Label{
- Name: label.Name,
- Color: label.Color,
- }
- }
|