From a1cb049afa65a7237d1d5f5a4edc11946ae64ee5 Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Wed, 14 Feb 2024 23:03:30 +0300 Subject: [PATCH] #8 implement interface Source and methods for gitea without Update --- cmd/pkgdashcli/main.go | 26 +- internal/configcli/config.go | 15 + internal/source/gitea/gitea.go | 614 +++++++++++++++++++++++++++++++ internal/source/github/github.go | 15 + internal/source/gitlab/gitlab.go | 15 + internal/source/gogs/gogs.go | 15 + internal/source/source.go | 28 ++ 7 files changed, 716 insertions(+), 12 deletions(-) create mode 100644 internal/configcli/config.go create mode 100644 internal/source/gitea/gitea.go create mode 100644 internal/source/github/github.go create mode 100644 internal/source/gitlab/gitlab.go create mode 100644 internal/source/gogs/gogs.go create mode 100644 internal/source/source.go diff --git a/cmd/pkgdashcli/main.go b/cmd/pkgdashcli/main.go index 053210e..dcfdf44 100644 --- a/cmd/pkgdashcli/main.go +++ b/cmd/pkgdashcli/main.go @@ -15,6 +15,7 @@ import ( "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" @@ -37,12 +38,6 @@ var ( DefaultPullRequestBody = `Bumps {{.Name}} from {{.VersionOld}} to {{.VersionNew}}` ) -type Config struct { - PullRequestTitle string `json:"pull_request_title" yaml:"pull_request_title"` - PullRequestBody string `json:"pull_request_body" yaml:"pull_request_body"` - Branches []string `json:"branches" yaml:"branches"` -} - var ( configFiles = []string{ "dependabot.yml", @@ -76,7 +71,7 @@ func main() { logger.Errorf(ctx, "logger init error: %v", err) } - cfg := &Config{} + cfg := &configcli.Config{} if err = config.Load(ctx, []config.Config{ @@ -154,12 +149,19 @@ func main() { modules.Updates(updateOptions) - repoMgmt := getRepoMgmt() - if repoMgmt == "unknown" { - logger.Fatalf(ctx, "failed to get repo management") + var repoGit, tokenGit string + if cfg.Source != nil { + repoGit = cfg.Source.TypeGit + tokenGit = cfg.Source.Token + } else { + repoGit = getRepoMgmt() + if repoGit == "unknown" { + logger.Fatalf(ctx, "failed to get repo management") + } } + _ = tokenGit - switch repoMgmt { + switch repoGit { case "gitea": for _, branch := range cfg.Branches { err = giteaPullRequest(ctx, cfg, branch, mvs) @@ -192,7 +194,7 @@ func getRepoMgmt() string { } } -func giteaPullRequest(ctx context.Context, cfg *Config, branch string, mods map[string]modules.Update) error { +func giteaPullRequest(ctx context.Context, cfg *configcli.Config, branch string, mods map[string]modules.Update) error { envAPIURL := os.Getenv("GITHUB_API_URL") envREPOSITORY := os.Getenv("GITHUB_REPOSITORY") envTOKEN := os.Getenv("GITHUB_TOKEN") diff --git a/internal/configcli/config.go b/internal/configcli/config.go new file mode 100644 index 0000000..07c5505 --- /dev/null +++ b/internal/configcli/config.go @@ -0,0 +1,15 @@ +package configcli + +type Config struct { + PullRequestTitle string `json:"pull_request_title" yaml:"pull_request_title"` + PullRequestBody string `json:"pull_request_body" yaml:"pull_request_body"` + Branches []string `json:"branches" yaml:"branches"` + Source *Source `json:"source" yaml:"source"` +} + +type Source struct { + TypeGit string `json:"type" yaml:"type"` + Token string `json:"token" yaml:"token"` + APIURL string `json:"apiurl" yaml:"apiurl"` + Repository string `json:"repository" yaml:"repository"` +} diff --git a/internal/source/gitea/gitea.go b/internal/source/gitea/gitea.go new file mode 100644 index 0000000..bbf5328 --- /dev/null +++ b/internal/source/gitea/gitea.go @@ -0,0 +1,614 @@ +package gitea + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "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" +) + +type Gitea struct { + Token string +} + +func NewGitea(t string) *Gitea { + return &Gitea{ + Token: t, + } +} + +type giteaPull struct { + URL string `json:"url"` + Title string `json:"title"` + Base struct { + Ref string `json:"ref"` + } `json:"base"` + ID int64 `json:"id"` +} + +func (g *Gitea) RequestOpen(ctx context.Context, cfg *configcli.Config, branch string, path string, mod modules.Update) error { + logger.Debugf(ctx, "RequestOpen start, mod title: %s", path) + + if cfg.Source == nil { + cfg.Source = &configcli.Source{ + TypeGit: "gitea", + Token: os.Getenv("GITHUB_TOKEN"), + APIURL: os.Getenv("GITHUB_API_URL"), + Repository: os.Getenv("GITHUB_REPOSITORY"), + } + } + + var buf []byte + var err error + // создания шаблона названия для пулл реквеста + tplTitle, err := template.New("pull_request_title").Parse(cfg.PullRequestTitle) + if err != nil { + logger.Fatalf(ctx, "failed to parse template: %v", err) + } + + wTitle := bytes.NewBuffer(nil) + // создания шаблона тела для пулл реквеста + tplBody, err := template.New("pull_request_body").Parse(cfg.PullRequestBody) + if err != nil { + logger.Fatalf(ctx, "failed to parse template: %v", err) + } + + wBody := bytes.NewBuffer(nil) + // открытие гит репозитория с опцией обхода репозитория для нахождения .git + repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + logger.Fatalf(ctx, "failed to open repo: %v", err) + } + //извлекаем ссылки с объектами из удаленного объекта?? + if err = repo.FetchContext(ctx, &git.FetchOptions{ + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Force: true, + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatalf(ctx, "failed to fetch repo: %v", err) + } + + var headRef *plumbing.Reference // вроде ссылка на гит + refIter, err := repo.Branches() //получение веток + if err != nil { + logger.Fatalf(ctx, "failed to get branches: %v", err) + } + for { + ref, err := refIter.Next() + if err != nil { + break + } + if ref.Name().String() == branch { + headRef = ref + break + } + } //перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef + refIter.Close() + + if headRef == nil { + logger.Fatalf(ctx, "failed to get repo branch head") + } // Не получили нужную ветку + + logger.Infof(ctx, "repo head %s", headRef) + + wtree, err := repo.Worktree() + if err != nil { + logger.Fatalf(ctx, "failed to get worktree: %v", err) + } + + var pulls []*giteaPull + req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.Source.APIURL+"/repos/"+cfg.Source.Repository+"/pulls?state=open&token="+cfg.Source.Token, nil) + if err != nil { + return err + } //вроде запроса к репозиторию + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + + rsp, err := http.DefaultClient.Do(req) // выполнение запроса + if err != nil { + return err + } + + buf, _ = io.ReadAll(rsp.Body) + if rsp.StatusCode != http.StatusOK { + return fmt.Errorf("unknown error: %s", buf) + } + + if err = json.Unmarshal(buf, &pulls); err != nil { + logger.Fatalf(ctx, "failed to decode response %s err: %v", buf, err) + } // записываем ответ от гита по пулл реквестам, видимо существующим + // перебираем наши модификации и если они уже есть в гите удаляем их из mods + + for _, pull := range pulls { + if strings.Contains(pull.Title, path) && pull.Base.Ref == branch { + logger.Infof(ctx, "skip %s as pr already exists %s", path, pull.URL) + return nil + } + } + + wTitle.Reset() + wBody.Reset() + + logger.Infof(ctx, "update %s from %s to %s", path, mod.Module.Version, mod.Version) + + logger.Infof(ctx, "reset worktree") + if err = wtree.Reset(&git.ResetOptions{Mode: git.HardReset}); err != nil { + logger.Fatalf(ctx, "failed to reset repo branch: %v", err) + } //вроде меняем ветку + + if err = wtree.PullContext(ctx, &git.PullOptions{ + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Depth: 1, + // RemoteURL : + Force: true, + RemoteName: "origin", + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatalf(ctx, "failed to pull repo: %v", err) + } + + logger.Infof(ctx, "checkout ref %s", headRef) + if err = wtree.Checkout(&git.CheckoutOptions{ + Hash: headRef.Hash(), + Branch: plumbing.NewBranchReferenceName(fmt.Sprintf("pkgdash-1/go_modules/%s-%s", path, mod.Version)), + Create: true, + Force: true, + }); err != nil { + logger.Fatalf(ctx, "failed to checkout tree: %v", err) + } //вроде как переходим на другую ветку + + epath, err := exec.LookPath("go") + if errors.Is(err, exec.ErrDot) { + err = nil + } + if err != nil { + logger.Fatalf(ctx, "failed to find go command: %v", err) + } // ищем go файл + + var cmd *exec.Cmd + var out []byte + + cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-require=%s@%s", path, mod.Version)) + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatalf(ctx, "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.Fatalf(ctx, "failed to run go mod tidy: %s err: %v", out, err) + } // пытаемся выполнить команду go mod tidy пытаемся подтянуть новую версию модуля + + logger.Infof(ctx, "worktree add go.mod") + if _, err = wtree.Add("go.mod"); err != nil { + logger.Fatalf(ctx, "failed to add file: %v", err) + } + + logger.Infof(ctx, "worktree add go.sum") + if _, err = wtree.Add("go.sum"); err != nil { + logger.Fatalf(ctx, "failed to add file: %v", err) + } + + logger.Infof(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.Fatalf(ctx, "failed to commit: %v", err) + } + + // newref := plumbing.NewHashReference(plumbing.ReferenceName(fmt.Sprintf("refs/heads/pkgdash-1/go_modules/%s-%s", path, mod.Version)), headRef.Hash()) + + /* + if err = repo.Storer.SetReference(newref); err != nil { + logger.Fatalf(ctx, "failed to create repo branch: %v", err) + } + */ + + refspec := gitconfig.RefSpec(fmt.Sprintf("+refs/heads/pkgdash-1/go_modules/%s-%s:refs/heads/pkgdash-1/go_modules/%s-%s", path, mod.Version, path, mod.Version)) + + logger.Infof(ctx, "try to push refspec %s", refspec) + + if err = repo.PushContext(ctx, &git.PushOptions{ + RefSpecs: []gitconfig.RefSpec{refspec}, + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Force: true, + }); err != nil { + logger.Fatalf(ctx, "failed to push repo branch: %v", err) + } // пытаемся за пушить изменения + + data := map[string]string{ + "Name": path, + "VersionOld": mod.Module.Version, + "VersionNew": mod.Version, + } + + if err = tplTitle.Execute(wTitle, data); err != nil { + logger.Fatalf(ctx, "failed to execute template: %v", err) + } + if err = tplBody.Execute(wBody, data); err != nil { + logger.Fatalf(ctx, "failed to execute template: %v", err) + } + + body := map[string]string{ + "base": branch, + "body": wBody.String(), + "head": fmt.Sprintf("pkgdash-1/go_modules/%s-%s", path, mod.Version), + "title": wTitle.String(), + } + logger.Infof(ctx, "raw body: %#+v", body) + + buf, err = json.Marshal(body) + if err != nil { + return err + } + + logger.Infof(ctx, "marshal body: %s", buf) + + req, err = http.NewRequestWithContext(ctx, http.MethodPost, cfg.Source.APIURL+"/repos/"+cfg.Source.Repository+"/pulls?token="+cfg.Source.Token, bytes.NewReader(buf)) + if err != nil { + return err + } + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + + 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) + } + + return nil +} + +func (g *Gitea) RequestClose(ctx context.Context, cfg *configcli.Config, branch string, path string) error { + logger.Debugf(ctx, "RequestOpen start, mod title: %s", path) + + if cfg.Source == nil { + cfg.Source = &configcli.Source{ + TypeGit: "gitea", + Token: os.Getenv("GITHUB_TOKEN"), + APIURL: os.Getenv("GITHUB_API_URL"), + Repository: os.Getenv("GITHUB_REPOSITORY"), + } + } + + var buf []byte + var err error + + repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + logger.Fatalf(ctx, "failed to open repo: %v", err) + } + //извлекаем ссылки с объектами из удаленного объекта?? + if err = repo.FetchContext(ctx, &git.FetchOptions{ + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Force: true, + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatalf(ctx, "failed to fetch repo: %v", err) + } + + var headRef *plumbing.Reference // вроде ссылка на гит + refIter, err := repo.Branches() //получение веток + if err != nil { + logger.Fatalf(ctx, "failed to get branches: %v", err) + } + for { + ref, err := refIter.Next() + if err != nil { + break + } + if ref.Name().String() == branch { + headRef = ref + break + } + } //перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef + refIter.Close() + + if headRef == nil { + logger.Fatalf(ctx, "failed to get repo branch head") + } // Не получили нужную ветку + + logger.Infof(ctx, "repo head %s", headRef) + + var pulls []*giteaPull + req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.Source.APIURL+"/repos/"+cfg.Source.Repository+"/pulls?state=open&token="+cfg.Source.Token, nil) + if err != nil { + return err + } //вроде запроса к репозиторию + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + + rsp, err := http.DefaultClient.Do(req) // выполнение запроса + if err != nil { + return err + } + + buf, _ = io.ReadAll(rsp.Body) + if rsp.StatusCode != http.StatusOK { + return fmt.Errorf("unknown error: %s", buf) + } + + if err = json.Unmarshal(buf, &pulls); err != nil { + logger.Fatalf(ctx, "failed to decode response %s err: %v", buf, err) + } // записываем ответ от гита по пулл реквестам, видимо существующим + // перебираем наши модификации и если они уже есть в гите удаляем их из mods + + for _, pull := range pulls { + if !strings.Contains(pull.Title, path) && pull.Base.Ref != branch { + logger.Infof(ctx, "skip %s since pr does not exist %s", path, pull.URL) + return nil + } + } + + if err = repo.DeleteBranch(path); err != nil { + logger.Errorf(ctx, "failed to delete the branch: %s", path) + return err + } + + logger.Infof(ctx, "Delete branch %s successful", path) + return nil +} + +func (g *Gitea) RequestUpdate(ctx context.Context, cfg *configcli.Config, branch string, path string, mod modules.Update) error { + logger.Debugf(ctx, "RequestOpen start, mod title: %s", path) + + if cfg.Source == nil { + cfg.Source = &configcli.Source{ + TypeGit: "gitea", + Token: os.Getenv("GITHUB_TOKEN"), + APIURL: os.Getenv("GITHUB_API_URL"), + Repository: os.Getenv("GITHUB_REPOSITORY"), + } + } + + var buf []byte + var err error + + // создания шаблона названия для пулл реквеста + tplTitle, err := template.New("pull_request_title").Parse(cfg.PullRequestTitle) + if err != nil { + logger.Fatalf(ctx, "failed to parse template: %v", err) + } + + wTitle := bytes.NewBuffer(nil) + // создания шаблона тела для пулл реквеста + tplBody, err := template.New("pull_request_body").Parse(cfg.PullRequestBody) + if err != nil { + logger.Fatalf(ctx, "failed to parse template: %v", err) + } + + wBody := bytes.NewBuffer(nil) + // открытие гит репозитория с опцией обхода репозитория для нахождения .git + repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + logger.Fatalf(ctx, "failed to open repo: %v", err) + } + //извлекаем ссылки с объектами из удаленного объекта?? + if err = repo.FetchContext(ctx, &git.FetchOptions{ + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Force: true, + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatalf(ctx, "failed to fetch repo: %v", err) + } + + var headRef *plumbing.Reference // вроде ссылка на гит + refIter, err := repo.Branches() //получение веток + if err != nil { + logger.Fatalf(ctx, "failed to get branches: %v", err) + } + for { + ref, err := refIter.Next() + if err != nil { + break + } + if ref.Name().String() == branch { + headRef = ref + break + } + } //перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef + refIter.Close() + + if headRef == nil { + logger.Fatalf(ctx, "failed to get repo branch head") + } // Не получили нужную ветку + + logger.Infof(ctx, "repo head %s", headRef) + + wtree, err := repo.Worktree() + if err != nil { + logger.Fatalf(ctx, "failed to get worktree: %v", err) + } + + var pulls []*giteaPull + req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.Source.APIURL+"/repos/"+cfg.Source.Repository+"/pulls?state=open&token="+cfg.Source.Token, nil) + if err != nil { + return err + } //вроде запроса к репозиторию + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + + rsp, err := http.DefaultClient.Do(req) // выполнение запроса + if err != nil { + return err + } + + buf, _ = io.ReadAll(rsp.Body) + if rsp.StatusCode != http.StatusOK { + return fmt.Errorf("unknown error: %s", buf) + } + + if err = json.Unmarshal(buf, &pulls); err != nil { + logger.Fatalf(ctx, "failed to decode response %s err: %v", buf, err) + } // записываем ответ от гита по пулл реквестам, видимо существующим + // перебираем наши модификации и если они уже есть в гите удаляем их из mods + + for _, pull := range pulls { + if !strings.Contains(pull.Title, path) && pull.Base.Ref != branch { + logger.Infof(ctx, "skip %s since pr does not exist %s", path, pull.URL) + return nil + } + } + + wTitle.Reset() + wBody.Reset() + + logger.Infof(ctx, "update %s from %s to %s", path, mod.Module.Version, mod.Version) + + logger.Infof(ctx, "reset worktree") + if err = wtree.Reset(&git.ResetOptions{Mode: git.HardReset}); err != nil { + logger.Fatalf(ctx, "failed to reset repo branch: %v", err) + } //вроде меняем ветку + + if err = wtree.PullContext(ctx, &git.PullOptions{ + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Depth: 1, + // RemoteURL : + Force: true, + RemoteName: "origin", + }); err != nil && err != git.NoErrAlreadyUpToDate { + logger.Fatalf(ctx, "failed to pull repo: %v", err) + } + + logger.Infof(ctx, "checkout ref %s", headRef) + if err = wtree.Checkout(&git.CheckoutOptions{ + Hash: headRef.Hash(), + Branch: headRef.Name(), + Create: false, + Force: true, + }); err != nil { + logger.Fatalf(ctx, "failed to checkout tree: %v", err) + } //вроде как переходим на другую ветку + + epath, err := exec.LookPath("go") + if errors.Is(err, exec.ErrDot) { + err = nil + } + if err != nil { + logger.Fatalf(ctx, "failed to find go command: %v", err) + } // ищем go файл + + var cmd *exec.Cmd + var out []byte + + cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-require=%s@%s", path, mod.Version)) + if out, err = cmd.CombinedOutput(); err != nil { + logger.Fatalf(ctx, "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.Fatalf(ctx, "failed to run go mod tidy: %s err: %v", out, err) + } // пытаемся выполнить команду go mod tidy пытаемся подтянуть новую версию модуля + + logger.Infof(ctx, "worktree add go.mod") + if _, err = wtree.Add("go.mod"); err != nil { + logger.Fatalf(ctx, "failed to add file: %v", err) + } + + logger.Infof(ctx, "worktree add go.sum") + if _, err = wtree.Add("go.sum"); err != nil { + logger.Fatalf(ctx, "failed to add file: %v", err) + } + + logger.Infof(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.Fatalf(ctx, "failed to commit: %v", err) + } + + // newref := plumbing.NewHashReference(plumbing.ReferenceName(fmt.Sprintf("refs/heads/pkgdash-1/go_modules/%s-%s", path, mod.Version)), headRef.Hash()) + + /* + if err = repo.Storer.SetReference(newref); err != nil { + logger.Fatalf(ctx, "failed to create repo branch: %v", err) + } + */ + + refspec := gitconfig.RefSpec(fmt.Sprintf("+refs/heads/pkgdash-1/go_modules/%s-%s:refs/heads/pkgdash-1/go_modules/%s-%s", path, mod.Version, path, mod.Version)) + + logger.Infof(ctx, "try to push refspec %s", refspec) + + if err = repo.PushContext(ctx, &git.PushOptions{ + RefSpecs: []gitconfig.RefSpec{refspec}, + Auth: &httpauth.BasicAuth{Username: cfg.Source.Token, Password: cfg.Source.Token}, + Force: true, + }); err != nil { + logger.Fatalf(ctx, "failed to push repo branch: %v", err) + } // пытаемся за пушить изменения + + data := map[string]string{ + "Name": path, + "VersionOld": mod.Module.Version, + "VersionNew": mod.Version, + } + + if err = tplTitle.Execute(wTitle, data); err != nil { + logger.Fatalf(ctx, "failed to execute template: %v", err) + } + if err = tplBody.Execute(wBody, data); err != nil { + logger.Fatalf(ctx, "failed to execute template: %v", err) + } + + body := map[string]string{ + "base": branch, + "body": wBody.String(), + "head": fmt.Sprintf("pkgdash-1/go_modules/%s-%s", path, mod.Version), + "title": wTitle.String(), + } + logger.Infof(ctx, "raw body: %#+v", body) + + buf, err = json.Marshal(body) + if err != nil { + return err + } + + logger.Infof(ctx, "marshal body: %s", buf) + + req, err = http.NewRequestWithContext(ctx, http.MethodPost, cfg.Source.APIURL+"/repos/"+cfg.Source.Repository+"/pulls?token="+cfg.Source.Token, bytes.NewReader(buf)) + if err != nil { + return err + } + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + + 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) + } + + return nil +} diff --git a/internal/source/github/github.go b/internal/source/github/github.go new file mode 100644 index 0000000..dd92fe7 --- /dev/null +++ b/internal/source/github/github.go @@ -0,0 +1,15 @@ +package github + +type Github struct { + Token string +} + +func NewGithub(t string) *Github { + return &Github{ + Token: t, + } +} + +func (g *Github) RequestOpen() {} +func (g *Github) RequestClose() {} +func (g *Github) RequestUpdate() {} diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go new file mode 100644 index 0000000..899284a --- /dev/null +++ b/internal/source/gitlab/gitlab.go @@ -0,0 +1,15 @@ +package gitlab + +type Gitlab struct { + Token string +} + +func NewGitlab(t string) *Gitlab { + return &Gitlab{ + Token: t, + } +} + +func (g *Gitlab) RequestOpen() {} +func (g *Gitlab) RequestClose() {} +func (g *Gitlab) RequestUpdate() {} diff --git a/internal/source/gogs/gogs.go b/internal/source/gogs/gogs.go new file mode 100644 index 0000000..e008d28 --- /dev/null +++ b/internal/source/gogs/gogs.go @@ -0,0 +1,15 @@ +package gogs + +type Gogs struct { + Token string +} + +func NewGogs(t string) *Gogs { + return &Gogs{ + Token: t, + } +} + +func (g *Gogs) RequestOpen() {} +func (g *Gogs) RequestClose() {} +func (g *Gogs) RequestUpdate() {} diff --git a/internal/source/source.go b/internal/source/source.go new file mode 100644 index 0000000..5f72dc2 --- /dev/null +++ b/internal/source/source.go @@ -0,0 +1,28 @@ +package source + +import ( + "git.unistack.org/unistack-org/pkgdash/internal/source/gitea" + "git.unistack.org/unistack-org/pkgdash/internal/source/github" + "git.unistack.org/unistack-org/pkgdash/internal/source/gitlab" + "git.unistack.org/unistack-org/pkgdash/internal/source/gogs" +) + +type SourceControl interface { + RequestOpen() + RequestClose() + RequestUpdate() +} + +func NewSourceControl(system, token string) SourceControl { + switch system { + case "github": + return github.NewGithub(token) + case "gitlab": + return gitlab.NewGitlab(token) + case "gitea": + return gitea.NewGitea(token) + case "gogs": + return gogs.NewGogs(token) + } + return nil +}