fixup to get working code

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2024-12-08 14:16:22 +03:00
parent 07f67fd1d7
commit 869f248d24
6 changed files with 489 additions and 110 deletions

View File

@@ -0,0 +1,48 @@
//go:build gogit
package git
/*
import "context"
type Repository interface {
Checkout(ctx context.Context, hash string) error
}
type repository struct {
path string
}
func NewRepositoryFromURL(ctx context.Context, url string) (Repository, error) {
return nil, nil
}
Branches() {
refIter, err := repo.Branches() // получение веток
if err != nil {
g.logger.Error(ctx, "failed to get branches", err)
return err
}
for {
ref, err := refIter.Next()
if err != nil {
if err == io.EOF {
break
}
g.logger.Error(ctx, "ref iter error", err)
return err
}
g.logger.Info(ctx, fmt.Sprintf("check %s == %s", ref.Name().Short(), branch))
if ref.Name().Short() == branch {
headRef = plumbing.NewHashReference(ref.Name(), ref.Hash())
g.logger.Info(ctx, "headRef set to "+headRef.String())
break
}
} // перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef
refIter.Close()
}
*/

View File

@@ -0,0 +1,262 @@
//go:build !gogit
package git
import (
"bytes"
"context"
"fmt"
"io"
"os/exec"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
)
type Repository interface {
Branches() ([]*plumbing.Reference, error)
// Auth(username string, password string) error
FetchContext(ctx context.Context, opts *git.FetchOptions) error
PushContext(ctx context.Context, opts *git.PushOptions) error
Head() (*plumbing.Reference, error)
Worktree() (Worktree, error)
}
type Worktree interface {
Checkout(*git.CheckoutOptions) error
PullContext(ctx context.Context, opts *git.PullOptions) error
Status() (git.Status, error)
AddWithOptions(opts *git.AddOptions) error
Commit(msg string, opts *git.CommitOptions) (plumbing.Hash, error)
Reset(opts *git.ResetOptions) error
}
type repository struct {
gocmd string
path string
// authUsername string
// authPassword string
}
func PlainOpenWithOptions(path string, opts *git.PlainOpenOptions) (Repository, error) {
gopath, err := exec.LookPath("git")
if err != nil {
return nil, err
}
return &repository{path: path, gocmd: gopath}, nil
}
/*
func (r *repository) Auth(username string, password string) error {
r.authUsername = username
r.authPassword = password
return nil
}
*/
func (r *repository) Branches() ([]*plumbing.Reference, error) {
var branches []*plumbing.Reference
cmd := exec.Command(r.gocmd, "show-ref", "--branches")
buf, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("output %s error %w", buf, err)
}
br := bytes.NewBuffer(buf)
for {
line, err := br.ReadString('\n')
if err != nil {
if err == io.EOF && line == "" {
break
} else if err != io.EOF && line == "" {
return nil, err
}
}
fields := strings.Fields(line)
if len(fields) != 2 {
return nil, fmt.Errorf("invalid fields %s", line)
}
branches = append(branches, plumbing.NewReferenceFromStrings(fields[1], fields[0]))
}
return branches, nil
}
func (r *repository) FetchContext(ctx context.Context, opts *git.FetchOptions) error {
args := []string{"fetch"}
if opts.Force {
args = append(args, "-f")
}
cmd := exec.CommandContext(ctx, r.gocmd, args...)
buf, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("output %s error %w", buf, err)
}
return nil
}
func (r *repository) PushContext(ctx context.Context, opts *git.PushOptions) error {
args := []string{"push"}
if opts.Force {
args = append(args, "-f")
}
/* TODO
var refs []string
for _, ref := range opts.RefSpecs {
refs = append(refs, ref.String())
}
args = append(args, strings.Join(refs, " "))
*/
cmd := exec.CommandContext(ctx, r.gocmd, args...)
buf, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("output %s error %w", buf, err)
}
return nil
}
func (r *repository) Head() (*plumbing.Reference, error) {
var head *plumbing.Reference
cmd := exec.Command(r.gocmd, "symbolic-ref", "--short", "HEAD")
buf, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("output %s error %w", buf, err)
}
br := bytes.NewBuffer(buf)
for {
line, err := br.ReadString('\n')
if err != nil {
if err == io.EOF && line == "" {
break
} else if err != io.EOF && line == "" {
return nil, err
}
}
fields := strings.Fields(line)
if len(fields) != 2 {
return nil, fmt.Errorf("invalid fields %s", line)
}
head = plumbing.NewReferenceFromStrings("HEAD", fields[0])
}
return head, nil
}
type worktree struct {
gocmd string
}
func (r *repository) Worktree() (Worktree, error) {
return &worktree{gocmd: r.gocmd}, nil
}
func (w *worktree) Checkout(opts *git.CheckoutOptions) error {
args := []string{"checkout"}
if opts.Create {
args = append(args, "-b", opts.Branch.Short())
}
if opts.Force {
args = append(args, "-f")
}
if opts.Hash.IsZero() {
args = append(args, opts.Branch.Short())
} else {
args = append(args, opts.Hash.String())
}
cmd := exec.Command(w.gocmd, args...)
buf, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("output %s error %w", buf, err)
}
return nil
}
func (w *worktree) Status() (git.Status, error) {
return git.Status{}, nil
}
func (w *worktree) Reset(opts *git.ResetOptions) error {
args := []string{"reset"}
if opts.Mode == git.HardReset {
args = append(args, "--hard")
}
args = append(args, opts.Commit.String())
cmd := exec.Command(w.gocmd, args...)
buf, err := cmd.CombinedOutput()
if err != nil {
return err
}
_ = buf
return nil
}
func (w *worktree) Commit(msg string, opts *git.CommitOptions) (plumbing.Hash, error) {
cmd := exec.Command(w.gocmd, `commit`,
fmt.Sprintf(`--author="%s <%s>"`, opts.Author.Name, opts.Author.Email),
"-m", msg,
fmt.Sprintf(`--date="%s"`, opts.Author.When.Format(`Mon Jan _2 15:04:05 2006 -0700`)),
)
buf, err := cmd.CombinedOutput()
if err != nil {
return plumbing.ZeroHash, fmt.Errorf("output %s error %w", buf, err)
}
var head *plumbing.Reference
cmd = exec.Command(w.gocmd, "show-ref", "HEAD")
buf, err = cmd.CombinedOutput()
if err != nil {
return plumbing.ZeroHash, err
}
br := bytes.NewBuffer(buf)
for {
line, err := br.ReadString('\n')
if err != nil {
if err == io.EOF && line == "" {
break
} else if err != io.EOF && line == "" {
return plumbing.ZeroHash, err
}
}
fields := strings.Fields(line)
if len(fields) != 2 {
return plumbing.ZeroHash, fmt.Errorf("invalid fields %s", line)
}
head = plumbing.NewReferenceFromStrings("HEAD", fields[0])
}
return head.Hash(), nil
}
func (w *worktree) PullContext(ctx context.Context, opts *git.PullOptions) error {
args := []string{"pull"}
if opts.Force {
args = append(args, "-f")
}
if opts.Depth != 0 {
args = append(args, fmt.Sprintf("--depth=%d", opts.Depth))
}
cmd := exec.CommandContext(ctx, w.gocmd, args...)
buf, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("output %s error %w", buf, err)
}
return nil
}
func (w *worktree) AddWithOptions(opts *git.AddOptions) error {
cmd := exec.Command(w.gocmd, "add", opts.Path)
buf, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("output %s error %w", buf, err)
}
return nil
}

View File

@@ -22,6 +22,7 @@ import (
"go.unistack.org/micro/v3/logger"
"go.unistack.org/pkgdash/internal/configcli"
"go.unistack.org/pkgdash/internal/modules"
gogit "go.unistack.org/pkgdash/internal/source/git"
)
var ErrPRNotExist = errors.New("pull request does not exist")
@@ -36,7 +37,6 @@ type Gitea struct {
Repository string
Owner string
pulls []*giteaPull
baseRef *plumbing.Reference
}
func NewGitea(cfg configcli.Config, log logger.Logger) *Gitea {
@@ -95,14 +95,16 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
}
if err = tplTitle.Execute(wTitle, data); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to execute template: %v", err))
g.logger.Error(ctx, "failed to execute template", err)
return err
}
if err = tplBody.Execute(wBody, data); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to execute template: %v", err))
g.logger.Error(ctx, "failed to execute template", err)
return err
}
// открытие гит репозитория с опцией обхода репозитория для нахождения .git
repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true})
repo, err := gogit.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to open repo: %v", err))
}
@@ -111,51 +113,43 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password},
Force: true,
}); err != nil && err != git.NoErrAlreadyUpToDate {
g.logger.Fatal(ctx, fmt.Sprintf("failed to fetch repo : %v", err))
g.logger.Error(ctx, "failed to fetch repo", err)
return err
} // обновляем репозиторий
var headRef *plumbing.Reference // вроде ссылка на гит
var headRef *plumbing.Reference
if g.baseRef == nil {
g.baseRef, err = repo.Head()
if err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("Error head: %s", err))
}
}
refIter, err := repo.Branches() // получение веток
branches, err := repo.Branches()
if err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to get branches: %v", err))
g.logger.Error(ctx, "cant get repo branches", err)
return err
}
for {
ref, err := refIter.Next()
if err != nil {
for _, ref := range branches {
if ref.Name().Short() == branch {
headRef = plumbing.NewHashReference(ref.Name(), ref.Hash())
g.logger.Info(ctx, "headRef set to "+headRef.String())
break
}
if ref.Name().Short() == branch { // todo вот тут возможно нужно переделать
headRef = ref
break
}
} // перебираем получение ветки и когда находим нужную выходим из цикла записав ветку в headRef
refIter.Close()
}
if headRef == nil {
g.logger.Fatal(ctx, "failed to get repo branch head")
g.logger.Error(ctx, "failed to get repo branch head")
return err
} // Не получили нужную ветку
g.logger.Info(ctx, fmt.Sprintf("repo head %s", headRef))
g.logger.Info(ctx, "repo head "+headRef.String())
wtree, err := repo.Worktree() // todo вроде рабочее дерево не нужно
if err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to get worktree: %v", err))
g.logger.Error(ctx, "failed to get worktree", err)
return err
}
defer g.checkout(*wtree, *g.baseRef)
g.pulls, err = g.GetPulls(ctx, g.URL, g.Owner, g.Repository, g.Password)
if err != nil && err != ErrPRNotExist {
g.logger.Error(ctx, fmt.Sprintf("GetPulls error: %s", err))
g.logger.Error(ctx, "GetPulls error", err)
return err
}
@@ -168,32 +162,47 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
g.logger.Info(ctx, fmt.Sprintf("update %s from %s to %s", path, mod.Module.Version, mod.Version))
g.logger.Info(ctx, "reset worktree")
if err = wtree.Reset(&git.ResetOptions{Commit: headRef.Hash(), Mode: git.HardReset}); err != nil {
g.logger.Error(ctx, fmt.Sprintf("failed to reset repo branch: %v", err))
} // вроде меняем ветку todo вроде можно удалить
wstatus, err := wtree.Status()
if err != nil {
g.logger.Error(ctx, "failed to get worktree status", err)
return err
}
g.logger.Info(ctx, "worktree status "+wstatus.String())
/*
g.logger.Info(ctx, "try to reset worktree to "+headRef.Hash().String())
if err = wtree.Reset(&git.ResetOptions{Commit: headRef.Hash(), Mode: git.HardReset}); err != nil {
g.logger.Error(ctx, "failed to reset repo branch to "+headRef.Hash().String(), err)
return err
} // вроде меняем ветку todo вроде можно удалить
*/
if err = wtree.PullContext(ctx, &git.PullOptions{
Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password},
Depth: 1,
Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password},
// Depth: 1,
// RemoteURL :
Force: true,
RemoteName: "origin",
}); err != nil && err != git.NoErrAlreadyUpToDate {
g.logger.Error(ctx, fmt.Sprintf("failed to pull repo: %v", err)) // подтягиваем изменения с удаленого репозитория
return err
}
g.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 && err != git.ErrBranchExists && err != git.ErrInvalidReference {
}); err != nil && err != git.ErrBranchExists {
g.logger.Error(ctx, fmt.Sprintf("failed to checkout tree: %v", err))
return err
} // создаем новую ветку
defer func() {
_ = g.checkout(wtree, headRef)
}()
epath, err := exec.LookPath("go")
if errors.Is(err, exec.ErrDot) {
err = nil
@@ -207,27 +216,32 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-droprequire=%s", mod.Module.Path))
if out, err = cmd.CombinedOutput(); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err))
g.logger.Error(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err))
return err
}
cmd = exec.CommandContext(ctx, epath, "mod", "edit", fmt.Sprintf("-require=%s@%s", path, mod.Version))
if out, err = cmd.CombinedOutput(); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err))
g.logger.Error(ctx, fmt.Sprintf("failed to run go mod edit: %s err: %v", out, err))
return err
} // пытаемся выполнить команду go mod edit с новой версией модуля
cmd = exec.CommandContext(ctx, epath, "mod", "tidy")
if out, err = cmd.CombinedOutput(); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to run go mod tidy: %s err: %v", out, err))
g.logger.Error(ctx, fmt.Sprintf("failed to run go mod tidy: %s err: %v", out, err))
return err
} // пытаемся выполнить команду go mod tidy пытаемся подтянуть новую версию модуля
g.logger.Info(ctx, "worktree add go.mod")
if _, err = wtree.Add("go.mod"); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to add file: %v", err))
if err = wtree.AddWithOptions(&git.AddOptions{Path: "go.mod"}); err != nil {
g.logger.Error(ctx, fmt.Sprintf("failed to add file: %v", err))
return err
}
g.logger.Info(ctx, "worktree add go.sum")
if _, err = wtree.Add("go.sum"); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to add file: %v", err))
if err = wtree.AddWithOptions(&git.AddOptions{Path: "go.sum"}); err != nil {
g.logger.Error(ctx, fmt.Sprintf("failed to add file: %v", err))
return err
}
g.logger.Info(ctx, "worktree commit")
@@ -240,7 +254,8 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
},
}) // хотим за коммитить изменения
if err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to commit: %v", err))
g.logger.Error(ctx, fmt.Sprintf("failed to commit: %v", err))
return 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 как будто нужно переделать
@@ -252,7 +267,8 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
Auth: &httpauth.BasicAuth{Username: g.Username, Password: g.Password},
Force: true,
}); err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to push repo branch: %v", err))
g.logger.Error(ctx, "failed to push repo branch", err)
return err
} // пытаемся за пушить изменения
body := map[string]string{
@@ -265,11 +281,11 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
buf, err = json.Marshal(body)
if err != nil {
g.logger.Error(ctx, "failed to marshal", err)
return err
}
g.logger.Info(ctx, fmt.Sprintf("marshal body: %s", buf))
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
@@ -277,6 +293,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
bytes.NewReader(buf),
)
if err != nil {
g.logger.Error(ctx, "http request error", err)
return err
}
req.Header.Add("Accept", "application/json")
@@ -285,6 +302,7 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
rsp, err := http.DefaultClient.Do(req)
if err != nil {
g.logger.Error(ctx, "failed to call http request", err)
return err
} // Вроде создаем новый реквест на создание пулл реквеста
if rsp.StatusCode != http.StatusCreated {
@@ -294,11 +312,6 @@ func (g *Gitea) RequestOpen(ctx context.Context, branch string, path string, mod
g.logger.Info(ctx, fmt.Sprintf("PR create for %s-%s", path, mod.Version))
repo, err = git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
g.logger.Fatal(ctx, fmt.Sprintf("failed to open repo: %v", err))
}
return nil
}
@@ -475,9 +488,9 @@ func (g *Gitea) GetPulls(ctx context.Context, url, owner, repo, password string)
return pullsAll, nil
}
func (g *Gitea) checkout(w git.Worktree, ref plumbing.Reference) {
func (g *Gitea) checkout(w gogit.Worktree, ref *plumbing.Reference) error {
ctx := context.Background()
g.logger.Debug(ctx, fmt.Sprintf("Checkout: %s", ref.Name().Short()))
g.logger.Debug(ctx, "checkout: "+ref.Name().Short())
if err := w.Checkout(&git.CheckoutOptions{
Branch: ref.Name(),
@@ -485,6 +498,8 @@ func (g *Gitea) checkout(w git.Worktree, ref plumbing.Reference) {
Force: true,
Keep: false,
}); err != nil {
g.logger.Error(ctx, fmt.Sprintf("failed to reset: %v", err))
g.logger.Error(ctx, "failed to reset", err)
return err
}
return nil
}