From 700ba16470cfbbd507b47706feb6d1e81cc55b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=B1=D1=83=D0=BD=D0=BE=D0=B2?= Date: Sat, 6 Apr 2024 15:18:43 +0300 Subject: [PATCH 1/8] =?UTF-8?q?#8=20=D0=9F=D0=BE=D0=BB=D1=83=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20project=20id=20=D0=B4=D0=BB=D1=8F=20gitlab?= =?UTF-8?q?=20(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vasiliy Tolstov Co-authored-by: Gorbunov Kirill Andreevich Reviewed-on: https://git.unistack.org/unistack-org/pkgdash/pulls/15 Co-authored-by: Кирилл Горбунов Co-committed-by: Кирилл Горбунов --- internal/source/gitea/gitea.go | 54 ++-- internal/source/github/github.go | 320 +++++++++++++++++++- internal/source/gitlab/gitlab.go | 496 ++++++++++++++++++++++++++++++- 3 files changed, 833 insertions(+), 37 deletions(-) diff --git a/internal/source/gitea/gitea.go b/internal/source/gitea/gitea.go index 1b84fd3..b318fd6 100644 --- a/internal/source/gitea/gitea.go +++ b/internal/source/gitea/gitea.go @@ -28,8 +28,8 @@ var ErrPRNotExist = errors.New("pull request does not exist") type Gitea struct { URL string - Username string - Password string + Username string + Password string PRTitle string PRBody string Repository string @@ -40,8 +40,8 @@ type Gitea struct { func NewGitea(cfg configcli.Config) *Gitea { return &Gitea{ URL: cfg.Source.APIURL, - Username: cfg.Source.Username, - Password: cfg.Source.Password, + Username: cfg.Source.Username, + Password: cfg.Source.Password, PRTitle: cfg.PullRequestTitle, PRBody: cfg.PullRequestBody, Repository: cfg.Source.Repository, @@ -105,10 +105,10 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod } //извлекаем ссылки с объектами из удаленного объекта?? if err = repo.FetchContext(ctx, &git.FetchOptions{ - // Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + // Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, Force: true, }); err != nil && err != git.NoErrAlreadyUpToDate { - logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v",err)) + logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) } //обновляем репозиторий var headRef *plumbing.Reference // вроде ссылка на гит @@ -140,8 +140,9 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } + defer checkout(wtree, *headRef) - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Username, g.Password) + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil && err != ErrPRNotExist { logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) return err @@ -157,7 +158,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod logger.Info(ctx, fmt.Sprintf("update %s from %s to %s", path, mod.Module.Version, mod.Version)) logger.Info(ctx, "reset worktree") - if err = wtree.Reset(&git.ResetOptions{Commit: headRef.Hash(), Mode: git.HardReset}); err != nil { + if err = wtree.Reset(&git.ResetOptions{Commit: headRef.Hash(), Mode: git.HardReset}); err != nil { logger.Error(ctx, fmt.Sprintf("failed to reset repo branch: %v", err)) } //вроде меняем ветку todo вроде можно удалить @@ -261,7 +262,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod req, err := http.NewRequestWithContext( ctx, http.MethodPost, - fmt.Sprintf("https://%s/api/v1/repos/%s/%s/pulls", g.URL, g.Owner, g.Repository, g.Password), + fmt.Sprintf("https://%s/api/v1/repos/%s/%s/pulls", g.URL, g.Owner, g.Repository), bytes.NewReader(buf), ) if err != nil { @@ -269,7 +270,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") -req.Header.Add("Authorization", g.Password) + req.Header.Add("Authorization", g.Password) rsp, err := http.DefaultClient.Do(req) if err != nil { @@ -293,7 +294,7 @@ req.Header.Add("Authorization", g.Password) func (g *Gitea) RequestClose(ctx context.Context, branch string, path string) error { logger.Debug(ctx, fmt.Sprintf("RequestClose start, mod title: %s", path)) - pulls, err := GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Username, g.Password) + pulls, err := GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil { logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) return err @@ -313,7 +314,7 @@ func (g *Gitea) RequestClose(ctx context.Context, branch string, path string) er return ErrPRNotExist } - req, err := DeleteBranch(ctx, g.URL, g.Owner, g.Repository, b, g.Username, g.Password) + req, err := DeleteBranch(ctx, g.URL, g.Owner, g.Repository, b, g.Password) if err != nil { logger.Error(ctx, fmt.Sprintf("failed to create request for delete the branch: %s, err: %s", branch, err)) return err @@ -333,7 +334,7 @@ func (g *Gitea) RequestUpdate(ctx context.Context, branch string, path string, m var err error if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Username, g.Password) + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil { logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) return err @@ -346,15 +347,15 @@ func (g *Gitea) RequestUpdate(ctx context.Context, branch string, path string, m logger.Info(ctx, fmt.Sprintf("don't skip %s since pr exist %s", path, pull.URL)) //todo tVersion := getVersions(pull.Head.Ref) //Надо взять просто из названия ветки последнюю версию if modules.IsNewerVersion(tVersion, mod.Version, false) { - reqDel, err := DeleteBranch(ctx, g.URL, g.Owner, g.Repository, pull.Head.Ref, g.Username, g.Password) + reqDel, err := DeleteBranch(ctx, g.URL, g.Owner, g.Repository, pull.Head.Ref, g.Password) if err != nil { logger.Error(ctx, fmt.Sprintf("Error with create request for branch: %s, err: %s", branch, err)) - continue + return err } rsp, err := http.DefaultClient.Do(reqDel) if err != nil { logger.Error(ctx, fmt.Sprintf("Error with do request for branch: %s, err: %s, code: %v", branch, err, rsp.StatusCode)) - continue //думаю что если не можем удалить ветку не стоит заканчивать работу, а перейти к следующей итерации + return err } logger.Info(ctx, fmt.Sprintf("Old pr %s successful delete", pull.Head.Ref)) } else { @@ -369,8 +370,6 @@ func (g *Gitea) RequestUpdate(ctx context.Context, branch string, path string, m return ErrPRNotExist } - logger.Info(ctx, fmt.Sprintf("update %s from %s to %s", path, mod.Module.Version, mod.Version)) - return g.RequestOpen(ctx, branch, path, mod) } @@ -378,7 +377,7 @@ func (g *Gitea) RequestList(ctx context.Context, branch string) (map[string]stri logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) var err error - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Username, g.Password) + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil { return nil, err } @@ -387,7 +386,7 @@ func (g *Gitea) RequestList(ctx context.Context, branch string) (map[string]stri rMap := make(map[string]string) for _, pull := range g.pulls { - if !strings.HasPrefix(pull.Title, "Bump ") { //добавляем только реквесты бота по обновлению модулей + if !strings.HasPrefix(pull.Title, "Bump ") || pull.Base.Ref != branch { //добавляем только реквесты бота по обновлению модулей continue } path = strings.Split(pull.Title, " ")[1] //todo Работет только для дефолтного шаблона @@ -404,7 +403,7 @@ func getVersions(s string) string { return version } -func DeleteBranch(ctx context.Context, url, owner, repo, branch, username, password string) (*http.Request, error) { +func DeleteBranch(ctx context.Context, url, owner, repo, branch, password string) (*http.Request, error) { var buf []byte req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fmt.Sprintf("https://%s/api/v1/repos/%s/%s/branches/%s", url, owner, repo, branch), bytes.NewReader(buf)) if err != nil { @@ -416,7 +415,7 @@ func DeleteBranch(ctx context.Context, url, owner, repo, branch, username, passw return req, err } -func GetPulls(ctx context.Context, url, owner, repo, username, password string) ([]*giteaPull, error) { +func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*giteaPull, error) { var pullsAll []*giteaPull page := 1 @@ -425,7 +424,7 @@ func GetPulls(ctx context.Context, url, owner, repo, username, password string) req, err := http.NewRequestWithContext( ctx, http.MethodGet, - fmt.Sprintf("https://%s/api/v1/repos/%s/%s/pulls?state=open&page=%v", url, owner, repo, page), + fmt.Sprintf("https://%s/api/v1/repos/%s/%s/pulls?state=open&page=%v", url, owner, repo, page), nil) if err != nil { return nil, err @@ -433,7 +432,7 @@ func GetPulls(ctx context.Context, url, owner, repo, username, password string) req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") -req.Header.Add("Authorization", password) + req.Header.Add("Authorization", password) rsp, err := http.DefaultClient.Do(req) // выполнение запроса if err != nil { @@ -464,3 +463,10 @@ req.Header.Add("Authorization", password) return pullsAll, nil } + +func checkout(w *git.Worktree, ref plumbing.Reference) { + ctx := context.Background() + if err := w.Reset(&git.ResetOptions{Commit: ref.Hash(), Mode: git.HardReset}); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) + } +} diff --git a/internal/source/github/github.go b/internal/source/github/github.go index 92c0254..22b355b 100644 --- a/internal/source/github/github.go +++ b/internal/source/github/github.go @@ -1,31 +1,292 @@ package github import ( + "bytes" "context" + "encoding/json" + "errors" "fmt" + "io" + "net/http" + "os/exec" + "strings" + "text/template" + "time" "git.unistack.org/unistack-org/pkgdash/internal/configcli" "git.unistack.org/unistack-org/pkgdash/internal/modules" + "github.com/go-git/go-git/v5" + gitconfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" + httpauth "github.com/go-git/go-git/v5/plumbing/transport/http" + "go.unistack.org/micro/v4/logger" ) +var ErrPRNotExist = errors.New("pull request does not exist") + type Github struct { - Username string - Password string + URL string + Username string + Password string + PRTitle string + PRBody string + Repository string + Owner string + pulls []*githubPull } func NewGithub(cfg configcli.Config) *Github { return &Github{ - Username: cfg.Source.Username, - Password: cfg.Source.Password, + URL: cfg.Source.APIURL, + Username: cfg.Source.Username, + Password: cfg.Source.Password, + PRTitle: cfg.PullRequestTitle, + PRBody: cfg.PullRequestBody, + Repository: cfg.Source.Repository, + Owner: cfg.Source.Owner, } } +type githubPull struct { + URL string `json:"url"` + Title string `json:"title"` + Base struct { + Ref string `json:"ref"` + } `json:"base"` + Head struct { + Ref string `json:"ref"` + } `json:"head"` + ID int64 `json:"id"` +} + func (g *Github) Name() string { return "github" } func (g *Github) RequestOpen(ctx context.Context, branch string, path string, mod modules.Update) error { - return fmt.Errorf("implement me") + logger.Debug(ctx, fmt.Sprintf("RequestOpen start, mod title: %s", path)) + + var buf []byte + var err error + // создания шаблона названия для пулл реквеста + tplTitle, err := template.New("pull_request_title").Parse(g.PRTitle) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to parse template: %v", err)) + } + + wTitle := bytes.NewBuffer(nil) + // создания шаблона тела для пулл реквеста + tplBody, err := template.New("pull_request_body").Parse(g.PRTitle) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to parse template: %v", err)) + } + + wBody := bytes.NewBuffer(nil) + + data := map[string]string{ + "Name": path, + "VersionOld": mod.Module.Version, + "VersionNew": mod.Version, + } + + if err = tplTitle.Execute(wTitle, data); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to execute template: %v", err)) + } + if err = tplBody.Execute(wBody, data); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to execute template: %v", err)) + } + + // открытие гит репозитория с опцией обхода репозитория для нахождения .git + repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to open repo: %v", err)) + } + //извлекаем ссылки с объектами из удаленного объекта?? + if err = repo.FetchContext(ctx, &git.FetchOptions{ + // Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Force: true, + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) + } //обновляем репозиторий + + var headRef *plumbing.Reference // вроде ссылка на гит + refIter, err := repo.Branches() //получение веток + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err)) + return err + } + for { + ref, err := refIter.Next() + if err != nil { + break + } + if ref.Name().Short() == branch { //todo вот тут возможно нужно переделать + headRef = ref + break + } + } //перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef + refIter.Close() + + if headRef == nil { + logger.Fatal(ctx, "failed to get repo branch head") + return err + } // Не получили нужную ветку + + logger.Info(ctx, fmt.Sprintf("repo head %s", headRef)) + + wtree, err := repo.Worktree() //todo вроде рабочее дерево не нужно + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) + } + + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } + + for _, pull := range g.pulls { + if strings.Contains(pull.Title, path) && strings.Contains(pull.Base.Ref, branch) { + logger.Info(ctx, fmt.Sprintf("PR for %s exists %s, call RequestUpdate", path, pull.URL)) + return g.RequestUpdate(ctx, branch, path, mod) + } // хотим проверить есть ли пулл реквест для этой ветки, если есть то выходим + } + + logger.Info(ctx, fmt.Sprintf("update %s from %s to %s", path, mod.Module.Version, mod.Version)) + + logger.Info(ctx, "reset worktree") + if err = wtree.Reset(&git.ResetOptions{Commit: headRef.Hash(), Mode: git.HardReset}); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to reset repo branch: %v", err)) + } //вроде меняем ветку todo вроде можно удалить + + if err = wtree.PullContext(ctx, &git.PullOptions{ + Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Depth: 1, + // RemoteURL : + Force: true, + RemoteName: "origin", + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Error(ctx, fmt.Sprintf("failed to pull repo: %v", err)) //подтягиваем изменения с удаленого репозитория + } + + logger.Info(ctx, fmt.Sprintf("checkout ref %s", headRef)) + if err = wtree.Checkout(&git.CheckoutOptions{ + Hash: headRef.Hash(), + Branch: plumbing.NewBranchReferenceName(fmt.Sprintf("pkgdash/go_modules/%s-%s", path, mod.Version)), + Create: true, + Force: true, + }); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to checkout tree: %v", err)) + return err + } //создаем новую ветку + + epath, err := exec.LookPath("go") + if errors.Is(err, exec.ErrDot) { + err = nil + } + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to find go command: %v", err)) + } // ищем go файл + + var cmd *exec.Cmd + var out []byte + + cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-droprequire=%s", mod.Module.Path)) + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err)) + } + + cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-require=%s@%s", path, mod.Version)) + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err)) + } // пытаемся выполнить команду go mod edit с новой версией модуля + + cmd = exec.CommandContext(ctx, epath, "mod", "tidy") + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to run go mod tidy: %s err: %v", out, err)) + } // пытаемся выполнить команду go mod tidy пытаемся подтянуть новую версию модуля + + logger.Info(ctx, "worktree add go.mod") + if _, err = wtree.Add("go.mod"); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to add file: %v", err)) + } + + logger.Info(ctx, "worktree add go.sum") + if _, err = wtree.Add("go.sum"); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to add file: %v", err)) + } + + logger.Info(ctx, "worktree commit") + _, err = wtree.Commit(wTitle.String(), &git.CommitOptions{ + Parents: []plumbing.Hash{headRef.Hash()}, + Author: &object.Signature{ + Name: "gitea-actions", + Email: "info@unistack.org", + When: time.Now(), + }, + }) // хотим за коммитить изменения + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to commit: %v", err)) + } + + refspec := gitconfig.RefSpec(fmt.Sprintf("+refs/heads/pkgdash/go_modules/%s-%s:refs/heads/pkgdash/go_modules/%s-%s", path, mod.Version, path, mod.Version)) //todo как будто нужно переделать + + logger.Info(ctx, fmt.Sprintf("try to push refspec %s", refspec)) + + if err = repo.PushContext(ctx, &git.PushOptions{ + RefSpecs: []gitconfig.RefSpec{refspec}, + Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Force: true, + }); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to push repo branch: %v", err)) + } // пытаемся за пушить изменения + + body := map[string]string{ + "base": branch, + "body": wBody.String(), + "head": fmt.Sprintf("pkgdash/go_modules/%s-%s", path, mod.Version), + "title": wTitle.String(), + } + logger.Info(ctx, fmt.Sprintf("raw body: %#+v", body)) + + buf, err = json.Marshal(body) + if err != nil { + return err + } + + logger.Info(ctx, fmt.Sprintf("marshal body: %s", buf)) + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + fmt.Sprintf("https://%s/api/v1/repos/%s/%s/pulls", g.URL, g.Owner, g.Repository), + bytes.NewReader(buf), + ) + if err != nil { + return err + } + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", g.Password) + + rsp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } //Вроде создаем новый реквест на создание пулл реквеста + if rsp.StatusCode != http.StatusCreated { + buf, _ = io.ReadAll(rsp.Body) + return fmt.Errorf("unknown error: %s", buf) + } + + logger.Info(ctx, fmt.Sprintf("PR create for %s-%s", path, mod.Version)) + + repo, err = git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to open repo: %v", err)) + } + + return nil } func (g *Github) RequestClose(ctx context.Context, branch string, path string) error { return fmt.Errorf("implement me") @@ -36,3 +297,52 @@ func (g *Github) RequestUpdate(ctx context.Context, branch string, path string, func (g *Github) RequestList(ctx context.Context, branch string) (map[string]string, error) { return nil, fmt.Errorf("implement me") } + +func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*githubPull, error) { + var pullsAll []*githubPull + page := 1 + + for { + pulls := make([]*githubPull, 0, 10) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("https://%s/api/v1/repos/%s/%s/pulls?state=open&page=%v", url, owner, repo, page), + nil) + if err != nil { + return nil, err + } //вроде запроса к репозиторию + + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", password) + + rsp, err := http.DefaultClient.Do(req) // выполнение запроса + if err != nil { + return nil, err + } + + buf, _ := io.ReadAll(rsp.Body) + + switch rsp.StatusCode { + case http.StatusOK: + if err = json.Unmarshal(buf, &pulls); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to decode response %s err: %v", buf, err)) + return nil, err + } + pullsAll = append(pullsAll, pulls...) + page++ + case http.StatusNotFound: + logger.Info(ctx, fmt.Sprintf("pull-request is not exist for %s", repo)) + return nil, ErrPRNotExist + default: + return nil, fmt.Errorf("unknown error: %s", buf) + } + + if len(pulls) == 0 { + break + } + } + + return pullsAll, nil +} diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 3528b13..a096eeb 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -1,38 +1,518 @@ package gitlab import ( + "bytes" "context" + "encoding/json" + "errors" "fmt" + "io" + "net/http" + "os/exec" + "regexp" + "strconv" + "strings" + "text/template" + "time" "git.unistack.org/unistack-org/pkgdash/internal/configcli" "git.unistack.org/unistack-org/pkgdash/internal/modules" + "github.com/go-git/go-git/v5" + gitconfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" + httpauth "github.com/go-git/go-git/v5/plumbing/transport/http" + "go.unistack.org/micro/v4/logger" ) +var ErrPRNotExist = errors.New("pull request does not exist") + type Gitlab struct { - Username string - Password string + URL string + Username string + Password string + PRTitle string + PRBody string + Repository string + RepositoryId string + Owner string + pulls []*gitlabPull } func NewGitlab(cfg configcli.Config) *Gitlab { return &Gitlab{ - Username: cfg.Source.Username, - Password: cfg.Source.Password, + URL: cfg.Source.APIURL, + Username: cfg.Source.Username, + Password: cfg.Source.Password, + PRTitle: cfg.PullRequestTitle, + PRBody: cfg.PullRequestBody, + Repository: cfg.Source.Repository, + Owner: cfg.Source.Owner, } } +type gitlabPull struct { + URL string `json:"web_url"` + Title string `json:"title"` + Target string `json:"target_branch"` + Source string `json:"source_branch"` + ID int64 `json:"id"` +} + +type gitlabProject struct { + Id int64 `json:"id"` + Name string `json:"name"` +} + func (g *Gitlab) Name() string { return "gitlab" } func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mod modules.Update) error { - return fmt.Errorf("implement me") + logger.Debug(ctx, fmt.Sprintf("RequestOpen start, mod title: %s", path)) + + var buf []byte + var err error + // создания шаблона названия для пулл реквеста + tplTitle, err := template.New("pull_request_title").Parse(g.PRTitle) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to parse template: %v", err)) + } + + wTitle := bytes.NewBuffer(nil) + // создания шаблона тела для пулл реквеста + tplBody, err := template.New("pull_request_body").Parse(g.PRTitle) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to parse template: %v", err)) + } + + wBody := bytes.NewBuffer(nil) + + data := map[string]string{ + "Name": path, + "VersionOld": mod.Module.Version, + "VersionNew": mod.Version, + } + + if err = tplTitle.Execute(wTitle, data); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to execute template: %v", err)) + } + if err = tplBody.Execute(wBody, data); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to execute template: %v", err)) + } + + // открытие гит репозитория с опцией обхода репозитория для нахождения .git + repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to open repo: %v", err)) + } + //извлекаем ссылки с объектами из удаленного объекта?? + if err = repo.FetchContext(ctx, &git.FetchOptions{ + // Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Force: true, + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) + } //обновляем репозиторий + + var headRef *plumbing.Reference // вроде ссылка на гит + + refIter, err := repo.Branches() //получение веток + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err)) + return err + } + for { + ref, err := refIter.Next() + if err != nil { + break + } + if ref.Name().Short() == branch { //todo вот тут возможно нужно переделать + headRef = ref + break + } + } //перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef + refIter.Close() + + if headRef == nil { + logger.Fatal(ctx, "failed to get repo branch head") + return err + } // Не получили нужную ветку + + logger.Info(ctx, fmt.Sprintf("repo head %s", headRef)) + + wtree, err := repo.Worktree() //todo вроде рабочее дерево не нужно + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) + } + defer checkout(wtree, *headRef) + + g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil || g.RepositoryId == "" { + return fmt.Errorf("project id is empty") + } + + g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } + + for _, pull := range g.pulls { + if strings.Contains(pull.Title, path) { + logger.Info(ctx, fmt.Sprintf("PR for %s exists %s, call RequestUpdate", path, pull.URL)) + return g.RequestUpdate(ctx, branch, path, mod) + } // хотим проверить есть ли пулл реквест для этой ветки, если есть то выходим + } + + logger.Info(ctx, fmt.Sprintf("update %s from %s to %s", path, mod.Module.Version, mod.Version)) + + sourceBranch := fmt.Sprintf("pkgdash/go_modules/%s-%s", path, mod.Version) + + logger.Info(ctx, "reset worktree") + if err = wtree.Reset(&git.ResetOptions{Commit: headRef.Hash(), Mode: git.HardReset}); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to reset repo branch: %v", err)) + } + + if err = wtree.PullContext(ctx, &git.PullOptions{ + Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Depth: 1, + // RemoteURL : + Force: true, + RemoteName: "origin", + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Error(ctx, fmt.Sprintf("failed to pull repo: %v", err)) //подтягиваем изменения с удаленого репозитория + } + + logger.Info(ctx, fmt.Sprintf("checkout ref %s", headRef)) + if err = wtree.Checkout(&git.CheckoutOptions{ + Hash: headRef.Hash(), + Branch: plumbing.NewBranchReferenceName(sourceBranch), + Create: true, + Force: true, + }); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to checkout tree: %v", err)) + return err + } //создаем новую ветку + + epath, err := exec.LookPath("go") + if errors.Is(err, exec.ErrDot) { + err = nil + } + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to find go command: %v", err)) + } // ищем go файл + + var cmd *exec.Cmd + var out []byte + + cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-droprequire=%s", mod.Module.Path)) + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err)) + } + + cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-require=%s@%s", path, mod.Version)) + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err)) + } // пытаемся выполнить команду go mod edit с новой версией модуля + + cmd = exec.CommandContext(ctx, epath, "mod", "tidy") + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to run go mod tidy: %s err: %v", out, err)) + } // пытаемся выполнить команду go mod tidy пытаемся подтянуть новую версию модуля + + logger.Info(ctx, "worktree add go.mod") + if _, err = wtree.Add("go.mod"); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to add file: %v", err)) + } + + logger.Info(ctx, "worktree add go.sum") + if _, err = wtree.Add("go.sum"); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to add file: %v", err)) + } + + logger.Info(ctx, "worktree commit") + _, err = wtree.Commit(wTitle.String(), &git.CommitOptions{ + Parents: []plumbing.Hash{headRef.Hash()}, + Author: &object.Signature{ + Name: "gitea-actions", + Email: "info@unistack.org", + When: time.Now(), + }, + }) // хотим за коммитить изменения + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to commit: %v", err)) + } + + refspec := gitconfig.RefSpec(fmt.Sprintf("+refs/heads/pkgdash/go_modules/%s-%s:refs/heads/pkgdash/go_modules/%s-%s", path, mod.Version, path, mod.Version)) //todo как будто нужно переделать + + logger.Info(ctx, fmt.Sprintf("try to push refspec %s", refspec)) + + if err = repo.PushContext(ctx, &git.PushOptions{ + RefSpecs: []gitconfig.RefSpec{refspec}, + Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Force: true, + }); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to push repo branch: %v", err)) + } // пытаемся за пушить изменения + + body := map[string]string{ + "id": g.RepositoryId, + "source_branch": sourceBranch, + "target_branch": branch, + "title": wTitle.String(), + "description": wBody.String(), + } + logger.Info(ctx, fmt.Sprintf("raw body: %#+v", body)) + + buf, err = json.Marshal(body) + if err != nil { + return err + } + + logger.Info(ctx, fmt.Sprintf("marshal body: %s", buf)) + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + fmt.Sprintf("https://%s/api/v4/projects/%s/merge_requests", g.URL, g.RepositoryId), + bytes.NewReader(buf), + ) + if err != nil { + return err + } + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", g.Password) + + rsp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } //Вроде создаем новый реквест на создание пулл реквеста + if rsp.StatusCode != http.StatusCreated { + buf, _ = io.ReadAll(rsp.Body) + return fmt.Errorf("unknown error: %s", buf) + } + + logger.Info(ctx, fmt.Sprintf("PR create for %s-%s", path, mod.Version)) + + return nil } + func (g *Gitlab) RequestClose(ctx context.Context, branch string, path string) error { - return fmt.Errorf("implement me") + logger.Debug(ctx, fmt.Sprintf("RequestClose start, mod title: %s", path)) + + var err error + + g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil || g.RepositoryId == "" { + return fmt.Errorf("project id is empty") + } + + pulls, err := GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } + + prExist := false + var b string // Name of the branch to be deleted + for _, pull := range pulls { + if strings.Contains(pull.Title, path) { + logger.Info(ctx, fmt.Sprintf("PR for %s exists: %s", path, pull.URL)) + prExist = true + b = pull.Source + } + } + if !prExist { + logger.Error(ctx, fmt.Sprintf("skip %s since pr does not exist", path)) + return ErrPRNotExist + } + + req, err := DeleteBranch(ctx, g.URL, g.RepositoryId, b, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("failed to create request for delete the branch: %s, err: %s", branch, err)) + return err + } + rsp, err := http.DefaultClient.Do(req) + if err != nil { + logger.Error(ctx, fmt.Sprintf("failed to do request for delete the branch: %s, err: %s, code: %v", branch, err, rsp.StatusCode)) + return err + } + + logger.Info(ctx, fmt.Sprintf("Delete branch for %s successful", path)) + return nil } + func (g *Gitlab) RequestUpdate(ctx context.Context, branch string, path string, mod modules.Update) error { - return fmt.Errorf("implement me") + logger.Debug(ctx, fmt.Sprintf("RequestUpdate start, mod title: %s", path)) + var err error + + g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil || g.RepositoryId == "" { + return fmt.Errorf("project id is empty") + } + + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } + } + + prExist := false + for _, pull := range g.pulls { + if strings.Contains(pull.Title, path) { + logger.Info(ctx, fmt.Sprintf("don't skip %s since pr exist %s", path, pull.URL)) //todo + tVersion := getVersions(pull.Source) //Надо взять просто из названия ветки последнюю версию + if modules.IsNewerVersion(tVersion, mod.Version, false) { + reqDel, err := DeleteBranch(ctx, g.URL, g.RepositoryId, pull.Source, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("Error with create request for branch: %s, err: %s", branch, err)) + return err + } + rsp, err := http.DefaultClient.Do(reqDel) + if err != nil { + logger.Error(ctx, fmt.Sprintf("Error with do request for branch: %s, err: %s, code: %v", branch, err, rsp.StatusCode)) + return err + } + logger.Info(ctx, fmt.Sprintf("Old pr %s successful delete", pull.Source)) + } else { + logger.Debug(ctx, "The existing PR is relevant") + return nil + } + prExist = true + } + } + if !prExist { + logger.Error(ctx, fmt.Sprintf("skip %s since pr does not exist", path)) + return ErrPRNotExist + } + + return g.RequestOpen(ctx, branch, path, mod) // todo это мне не нравится } + func (g *Gitlab) RequestList(ctx context.Context, branch string) (map[string]string, error) { - return nil, fmt.Errorf("implement me") + logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) + var err error + + g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil || g.RepositoryId == "" { + return nil, fmt.Errorf("project id is empty") + } + + g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil { + return nil, err + } + + var path string + rMap := make(map[string]string) + + for _, pull := range g.pulls { + if !strings.HasPrefix(pull.Title, "Bump ") { //добавляем только реквесты бота по обновлению модулей + continue + } + path = strings.Split(pull.Title, " ")[1] //todo Работет только для дефолтного шаблона + rMap[path] = pull.Title + } + return rMap, nil +} + +func getVersions(s string) string { + re := regexp.MustCompile("[vV][0-9]+\\.[0-9]+\\.[0-9]+") + + version := re.FindString(s) + + return version +} + +func DeleteBranch(ctx context.Context, url, projectId, branch, password string) (*http.Request, error) { + var buf []byte + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fmt.Sprintf("https://%s/api/v4/projects/%s/repository/branches/%s", url, projectId, branch), bytes.NewReader(buf)) + if err != nil { + return nil, err + } + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", password) + return req, err +} + +func GetPulls(ctx context.Context, url, projectId, branch, password string) ([]*gitlabPull, error) { + pulls := make([]*gitlabPull, 0, 10) + req, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("https://%s/api/v4/projects/%s/merge_requests?state=opened&target_branch=%s", url, projectId, branch), + nil) + if err != nil { + return nil, err + } //вроде запроса к репозиторию + + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", password) + + rsp, err := http.DefaultClient.Do(req) // выполнение запроса + if err != nil { + return nil, err + } + + buf, _ := io.ReadAll(rsp.Body) + + switch rsp.StatusCode { + case http.StatusOK: + if err = json.Unmarshal(buf, &pulls); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to decode response %s err: %v", buf, err)) + return nil, err + } + return pulls, nil + case http.StatusNotFound: + logger.Info(ctx, fmt.Sprintf("pull-request is not exist for %s", projectId)) + return nil, ErrPRNotExist + default: + return nil, fmt.Errorf("unknown error: %s", buf) + } +} + +func GetRepoID(ctx context.Context, url, owner, repo, password string) (rId string, err error) { + var buf []byte + projects := make([]*gitlabProject, 0, 10) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://%s/api/v4/users/%s/projects?owned=true", url, owner), nil) + if err != nil { + return + } + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", password) + + rsp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + + buf, _ = io.ReadAll(rsp.Body) + + switch rsp.StatusCode { + case http.StatusOK: + if err = json.Unmarshal(buf, &projects); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to decode response %s err: %v", buf, err)) + } + for _, p := range projects { + if p.Name == repo { + rId = strconv.Itoa(int(p.Id)) + } + } + return + default: + return rId, fmt.Errorf("unknown error: %s", buf) + } +} + +func checkout(w *git.Worktree, ref plumbing.Reference) { + ctx := context.Background() + if err := w.Reset(&git.ResetOptions{Commit: ref.Hash(), Mode: git.HardReset}); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) + } } From de2eafaac51a9e2b7bea2665faff7d3534aedb12 Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Mon, 8 Apr 2024 23:37:21 +0300 Subject: [PATCH 2/8] #8 Checkout, auth. --- .gitea/pkgdashcli.yaml | 3 +- internal/source/gitea/gitea.go | 53 +++++++++------ internal/source/github/github.go | 37 ++++++++-- internal/source/gitlab/gitlab.go | 112 +++++++++---------------------- 4 files changed, 98 insertions(+), 107 deletions(-) diff --git a/.gitea/pkgdashcli.yaml b/.gitea/pkgdashcli.yaml index aa426c2..fe0f9e9 100644 --- a/.gitea/pkgdashcli.yaml +++ b/.gitea/pkgdashcli.yaml @@ -3,8 +3,9 @@ branches: source: type: gitea apiurl: git.unistack.org + repository: pkgdash update_opt: pre: false major: false up_major: false - cached: true + cached: true \ No newline at end of file diff --git a/internal/source/gitea/gitea.go b/internal/source/gitea/gitea.go index b318fd6..7a05e69 100644 --- a/internal/source/gitea/gitea.go +++ b/internal/source/gitea/gitea.go @@ -105,13 +105,18 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod } //извлекаем ссылки с объектами из удаленного объекта?? if err = repo.FetchContext(ctx, &git.FetchOptions{ - // Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, + Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password}, Force: true, }); err != nil && err != git.NoErrAlreadyUpToDate { logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) } //обновляем репозиторий - var headRef *plumbing.Reference // вроде ссылка на гит + var headRef, baseRef *plumbing.Reference // вроде ссылка на гит + + baseRef, err = repo.Head() + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("Error head: %s", err)) + } refIter, err := repo.Branches() //получение веток if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err)) @@ -140,12 +145,14 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } - defer checkout(wtree, *headRef) + defer checkout(wtree, *baseRef) - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } } for _, pull := range g.pulls { @@ -270,7 +277,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", g.Password) + req.Header.Add("Authorization", "Bearer "+g.Password) rsp, err := http.DefaultClient.Do(req) if err != nil { @@ -293,16 +300,19 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod func (g *Gitea) RequestClose(ctx context.Context, branch string, path string) error { logger.Debug(ctx, fmt.Sprintf("RequestClose start, mod title: %s", path)) + var err error - pulls, err := GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } } prExist := false var b string // Name of the branch to be deleted - for _, pull := range pulls { + for _, pull := range g.pulls { if strings.Contains(pull.Title, path) && pull.Base.Ref == branch { logger.Info(ctx, fmt.Sprintf("PR for %s exists: %s", path, pull.URL)) prExist = true @@ -377,9 +387,12 @@ func (g *Gitea) RequestList(ctx context.Context, branch string) (map[string]stri logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) var err error - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil { - return nil, err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return nil, err + } } var path string @@ -411,7 +424,7 @@ func DeleteBranch(ctx context.Context, url, owner, repo, branch, password string } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", password) + req.Header.Add("Authorization", "Bearer "+password) return req, err } @@ -432,7 +445,7 @@ func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*giteaP req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", password) + req.Header.Add("Authorization", "Bearer "+password) rsp, err := http.DefaultClient.Do(req) // выполнение запроса if err != nil { @@ -466,7 +479,9 @@ func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*giteaP func checkout(w *git.Worktree, ref plumbing.Reference) { ctx := context.Background() - if err := w.Reset(&git.ResetOptions{Commit: ref.Hash(), Mode: git.HardReset}); err != nil { + logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) + + if err := w.Checkout(&git.CheckoutOptions{Hash: ref.Hash()}); err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) } } diff --git a/internal/source/github/github.go b/internal/source/github/github.go index 22b355b..b1b6fda 100644 --- a/internal/source/github/github.go +++ b/internal/source/github/github.go @@ -140,10 +140,12 @@ func (g *Github) RequestOpen(ctx context.Context, branch string, path string, mo logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } } for _, pull := range g.pulls { @@ -268,7 +270,7 @@ func (g *Github) RequestOpen(ctx context.Context, branch string, path string, mo } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", g.Password) + req.Header.Add("Authorization", "Bearer "+g.Password) rsp, err := http.DefaultClient.Do(req) if err != nil { @@ -295,7 +297,28 @@ func (g *Github) RequestUpdate(ctx context.Context, branch string, path string, return fmt.Errorf("implement me") } func (g *Github) RequestList(ctx context.Context, branch string) (map[string]string, error) { - return nil, fmt.Errorf("implement me") + logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) + var err error + + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return nil, err + } + } + + var path string + rMap := make(map[string]string) + + for _, pull := range g.pulls { + if !strings.HasPrefix(pull.Title, "Bump ") || pull.Base.Ref != branch { //добавляем только реквесты бота по обновлению модулей + continue + } + path = strings.Split(pull.Title, " ")[1] //todo Работет только для дефолтного шаблона + rMap[path] = pull.Title + } + return rMap, nil } func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*githubPull, error) { @@ -315,7 +338,7 @@ func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*github req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", password) + req.Header.Add("Authorization", "Bearer "+password) rsp, err := http.DefaultClient.Do(req) // выполнение запроса if err != nil { diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index a096eeb..1091d53 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -10,7 +10,6 @@ import ( "net/http" "os/exec" "regexp" - "strconv" "strings" "text/template" "time" @@ -59,11 +58,6 @@ type gitlabPull struct { ID int64 `json:"id"` } -type gitlabProject struct { - Id int64 `json:"id"` - Name string `json:"name"` -} - func (g *Gitlab) Name() string { return "gitlab" } @@ -114,12 +108,16 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) } //обновляем репозиторий - var headRef *plumbing.Reference // вроде ссылка на гит + var headRef, baseRef *plumbing.Reference // вроде ссылка на гит + + baseRef, err = repo.Head() + if err != nil { + logger.Error(ctx, fmt.Sprintf("Error head: %s", err)) + } refIter, err := repo.Branches() //получение веток if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err)) - return err } for { ref, err := refIter.Next() @@ -144,17 +142,14 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } - defer checkout(wtree, *headRef) + defer checkout(wtree, *baseRef) - g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil || g.RepositoryId == "" { - return fmt.Errorf("project id is empty") - } - - g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } } for _, pull := range g.pulls { @@ -282,7 +277,7 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", g.Password) + req.Header.Add("Authorization", "Bearer "+g.Password) rsp, err := http.DefaultClient.Do(req) if err != nil { @@ -300,23 +295,19 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo func (g *Gitlab) RequestClose(ctx context.Context, branch string, path string) error { logger.Debug(ctx, fmt.Sprintf("RequestClose start, mod title: %s", path)) - var err error - g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil || g.RepositoryId == "" { - return fmt.Errorf("project id is empty") - } - - pulls, err := GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err + } } prExist := false var b string // Name of the branch to be deleted - for _, pull := range pulls { + for _, pull := range g.pulls { if strings.Contains(pull.Title, path) { logger.Info(ctx, fmt.Sprintf("PR for %s exists: %s", path, pull.URL)) prExist = true @@ -347,11 +338,6 @@ func (g *Gitlab) RequestUpdate(ctx context.Context, branch string, path string, logger.Debug(ctx, fmt.Sprintf("RequestUpdate start, mod title: %s", path)) var err error - g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil || g.RepositoryId == "" { - return fmt.Errorf("project id is empty") - } - if len(g.pulls) == 0 { g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) if err != nil { @@ -396,14 +382,12 @@ func (g *Gitlab) RequestList(ctx context.Context, branch string) (map[string]str logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) var err error - g.RepositoryId, err = GetRepoID(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil || g.RepositoryId == "" { - return nil, fmt.Errorf("project id is empty") - } - - g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil { - return nil, err + if len(g.pulls) == 0 { + g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) + if err != nil && err != ErrPRNotExist { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return nil, err + } } var path string @@ -435,7 +419,7 @@ func DeleteBranch(ctx context.Context, url, projectId, branch, password string) } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", password) + req.Header.Add("Authorization", "Bearer "+password) return req, err } @@ -452,7 +436,7 @@ func GetPulls(ctx context.Context, url, projectId, branch, password string) ([]* req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", password) + req.Header.Add("Authorization", "Bearer "+password) rsp, err := http.DefaultClient.Do(req) // выполнение запроса if err != nil { @@ -476,43 +460,11 @@ func GetPulls(ctx context.Context, url, projectId, branch, password string) ([]* } } -func GetRepoID(ctx context.Context, url, owner, repo, password string) (rId string, err error) { - var buf []byte - projects := make([]*gitlabProject, 0, 10) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://%s/api/v4/users/%s/projects?owned=true", url, owner), nil) - if err != nil { - return - } - req.Header.Add("Accept", "application/json") - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", password) - - rsp, err := http.DefaultClient.Do(req) - if err != nil { - return - } - - buf, _ = io.ReadAll(rsp.Body) - - switch rsp.StatusCode { - case http.StatusOK: - if err = json.Unmarshal(buf, &projects); err != nil { - logger.Error(ctx, fmt.Sprintf("failed to decode response %s err: %v", buf, err)) - } - for _, p := range projects { - if p.Name == repo { - rId = strconv.Itoa(int(p.Id)) - } - } - return - default: - return rId, fmt.Errorf("unknown error: %s", buf) - } -} - func checkout(w *git.Worktree, ref plumbing.Reference) { ctx := context.Background() - if err := w.Reset(&git.ResetOptions{Commit: ref.Hash(), Mode: git.HardReset}); err != nil { + logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) + + if err := w.Checkout(&git.CheckoutOptions{Hash: ref.Hash()}); err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) } } From 2309bba07e7886ecf03a0e0bcd67cf30db93e559 Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Mon, 8 Apr 2024 23:49:57 +0300 Subject: [PATCH 3/8] #8 GetPulls. --- internal/source/gitea/gitea.go | 40 +++++++++++++------------------- internal/source/github/github.go | 20 +++++++--------- internal/source/gitlab/gitlab.go | 40 +++++++++++++------------------- 3 files changed, 40 insertions(+), 60 deletions(-) diff --git a/internal/source/gitea/gitea.go b/internal/source/gitea/gitea.go index 7a05e69..01c843a 100644 --- a/internal/source/gitea/gitea.go +++ b/internal/source/gitea/gitea.go @@ -147,12 +147,10 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod } defer checkout(wtree, *baseRef) - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } for _, pull := range g.pulls { @@ -302,12 +300,10 @@ func (g *Gitea) RequestClose(ctx context.Context, branch string, path string) er logger.Debug(ctx, fmt.Sprintf("RequestClose start, mod title: %s", path)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } prExist := false @@ -343,12 +339,10 @@ func (g *Gitea) RequestUpdate(ctx context.Context, branch string, path string, m logger.Debug(ctx, fmt.Sprintf("RequestUpdate start, mod title: %s", path)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } prExist := false @@ -387,12 +381,10 @@ func (g *Gitea) RequestList(ctx context.Context, branch string) (map[string]stri logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return nil, err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return nil, err } var path string diff --git a/internal/source/github/github.go b/internal/source/github/github.go index b1b6fda..ad796cc 100644 --- a/internal/source/github/github.go +++ b/internal/source/github/github.go @@ -140,12 +140,10 @@ func (g *Github) RequestOpen(ctx context.Context, branch string, path string, mo logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } for _, pull := range g.pulls { @@ -300,12 +298,10 @@ func (g *Github) RequestList(ctx context.Context, branch string) (map[string]str logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return nil, err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return nil, err } var path string diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 1091d53..07263a5 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -144,12 +144,10 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo } defer checkout(wtree, *baseRef) - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } for _, pull := range g.pulls { @@ -297,12 +295,10 @@ func (g *Gitlab) RequestClose(ctx context.Context, branch string, path string) e logger.Debug(ctx, fmt.Sprintf("RequestClose start, mod title: %s", path)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } prExist := false @@ -338,12 +334,10 @@ func (g *Gitlab) RequestUpdate(ctx context.Context, branch string, path string, logger.Debug(ctx, fmt.Sprintf("RequestUpdate start, mod title: %s", path)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return err } prExist := false @@ -382,12 +376,10 @@ func (g *Gitlab) RequestList(ctx context.Context, branch string) (map[string]str logger.Debug(ctx, fmt.Sprintf("RequestList for %s", branch)) var err error - if len(g.pulls) == 0 { - g.pulls, err = GetPulls(ctx, g.URL, g.RepositoryId, branch, g.Password) - if err != nil && err != ErrPRNotExist { - logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) - return nil, err - } + g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) + if err != nil { + logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err)) + return nil, err } var path string From c0887adf996c79b8fd4681ce184504708e413b05 Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Tue, 9 Apr 2024 00:04:57 +0300 Subject: [PATCH 4/8] #8 checkout on first head. --- internal/source/gitea/gitea.go | 14 +++++++++----- internal/source/github/github.go | 19 +++++++++++++++++++ internal/source/gitlab/gitlab.go | 13 ++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/internal/source/gitea/gitea.go b/internal/source/gitea/gitea.go index 01c843a..99c96e3 100644 --- a/internal/source/gitea/gitea.go +++ b/internal/source/gitea/gitea.go @@ -35,6 +35,7 @@ type Gitea struct { Repository string Owner string pulls []*giteaPull + baseRef *plumbing.Reference } func NewGitea(cfg configcli.Config) *Gitea { @@ -111,12 +112,15 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) } //обновляем репозиторий - var headRef, baseRef *plumbing.Reference // вроде ссылка на гит + var headRef *plumbing.Reference // вроде ссылка на гит - baseRef, err = repo.Head() - if err != nil { - logger.Fatal(ctx, fmt.Sprintf("Error head: %s", err)) + if g.baseRef == nil { + g.baseRef, err = repo.Head() + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("Error head: %s", err)) + } } + refIter, err := repo.Branches() //получение веток if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err)) @@ -145,7 +149,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } - defer checkout(wtree, *baseRef) + defer checkout(wtree, *g.baseRef) g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil { diff --git a/internal/source/github/github.go b/internal/source/github/github.go index ad796cc..9c15271 100644 --- a/internal/source/github/github.go +++ b/internal/source/github/github.go @@ -34,6 +34,7 @@ type Github struct { Repository string Owner string pulls []*githubPull + baseRef *plumbing.Reference } func NewGithub(cfg configcli.Config) *Github { @@ -111,6 +112,14 @@ func (g *Github) RequestOpen(ctx context.Context, branch string, path string, mo } //обновляем репозиторий var headRef *plumbing.Reference // вроде ссылка на гит + + if g.baseRef == nil { + g.baseRef, err = repo.Head() + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("Error head: %s", err)) + } + } + refIter, err := repo.Branches() //получение веток if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err)) @@ -139,6 +148,7 @@ func (g *Github) RequestOpen(ctx context.Context, branch string, path string, mo if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } + defer checkout(wtree, *g.baseRef) g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil { @@ -365,3 +375,12 @@ func GetPulls(ctx context.Context, url, owner, repo, password string) ([]*github return pullsAll, nil } + +func checkout(w *git.Worktree, ref plumbing.Reference) { + ctx := context.Background() + logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) + + if err := w.Checkout(&git.CheckoutOptions{Hash: ref.Hash()}); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) + } +} diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 07263a5..4b1898c 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -36,6 +36,7 @@ type Gitlab struct { RepositoryId string Owner string pulls []*gitlabPull + baseRef *plumbing.Reference } func NewGitlab(cfg configcli.Config) *Gitlab { @@ -108,11 +109,13 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err)) } //обновляем репозиторий - var headRef, baseRef *plumbing.Reference // вроде ссылка на гит + var headRef *plumbing.Reference // вроде ссылка на гит - baseRef, err = repo.Head() - if err != nil { - logger.Error(ctx, fmt.Sprintf("Error head: %s", err)) + if g.baseRef == nil { + g.baseRef, err = repo.Head() + if err != nil { + logger.Fatal(ctx, fmt.Sprintf("Error head: %s", err)) + } } refIter, err := repo.Branches() //получение веток @@ -142,7 +145,7 @@ func (g *Gitlab) RequestOpen(ctx context.Context, branch string, path string, mo if err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err)) } - defer checkout(wtree, *baseRef) + defer checkout(wtree, *g.baseRef) g.pulls, err = GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password) if err != nil { From 11d6e7d29ecf71ed97dad3aef8acbcfe6367e73e Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Tue, 9 Apr 2024 00:08:19 +0300 Subject: [PATCH 5/8] #8 change checkout. --- internal/source/gitlab/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 4b1898c..5ac9a4e 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -459,7 +459,7 @@ func checkout(w *git.Worktree, ref plumbing.Reference) { ctx := context.Background() logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) - if err := w.Checkout(&git.CheckoutOptions{Hash: ref.Hash()}); err != nil { + if err := w.Checkout(&git.CheckoutOptions{Branch: ref.Name()}); err != nil { logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) } } From 85d580a63ec4577966bb867f0e83c1bfb975b0d4 Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Tue, 9 Apr 2024 00:11:50 +0300 Subject: [PATCH 6/8] #8 change checkout. --- internal/source/gitlab/gitlab.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 5ac9a4e..7ef1410 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -459,7 +459,7 @@ func checkout(w *git.Worktree, ref plumbing.Reference) { ctx := context.Background() logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) - if err := w.Checkout(&git.CheckoutOptions{Branch: ref.Name()}); err != nil { - logger.Fatal(ctx, fmt.Sprintf("failed to reset: %v", err)) + if err := w.Checkout(&git.CheckoutOptions{Branch: ref.Name(), Create: false, Force: true}); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to reset: %v", err)) } } From a18e12bfc473e462fbbcc93f1c6ff9d3474701fe Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Tue, 9 Apr 2024 00:40:13 +0300 Subject: [PATCH 7/8] #8 change checkout. --- internal/source/gitlab/gitlab.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 7ef1410..095fa6c 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -459,7 +459,9 @@ func checkout(w *git.Worktree, ref plumbing.Reference) { ctx := context.Background() logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) - if err := w.Checkout(&git.CheckoutOptions{Branch: ref.Name(), Create: false, Force: true}); err != nil { + if err := w.Checkout(&git.CheckoutOptions{ + Branch: plumbing.ReferenceName(ref.Name().String()), + }); err != nil { logger.Error(ctx, fmt.Sprintf("failed to reset: %v", err)) } } From d7ace1aef4dc60f424c1ec39d637f7c1faea027c Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Tue, 9 Apr 2024 00:44:19 +0300 Subject: [PATCH 8/8] #8 change checkout. --- internal/source/gitlab/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index 095fa6c..64aabdf 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -460,7 +460,7 @@ func checkout(w *git.Worktree, ref plumbing.Reference) { logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short())) if err := w.Checkout(&git.CheckoutOptions{ - Branch: plumbing.ReferenceName(ref.Name().String()), + Branch: ref.Name(), }); err != nil { logger.Error(ctx, fmt.Sprintf("failed to reset: %v", err)) }