2024-03-24 20:52:32 +03:00
package gitea
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os/exec"
"regexp"
"strings"
"text/template"
"time"
"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"
2024-12-07 02:35:30 +03:00
"go.unistack.org/micro/v3/logger"
"go.unistack.org/pkgdash/internal/configcli"
"go.unistack.org/pkgdash/internal/modules"
2024-12-18 18:01:40 +03:00
//gogit "go.unistack.org/pkgdash/internal/source/git"
2024-03-24 20:52:32 +03:00
)
2024-03-25 22:54:25 +03:00
var ErrPRNotExist = errors . New ( "pull request does not exist" )
2024-03-24 20:52:32 +03:00
type Gitea struct {
2024-12-07 02:35:30 +03:00
logger logger . Logger
2024-03-24 20:52:32 +03:00
URL string
2024-04-06 15:18:43 +03:00
Username string
Password string
2024-03-24 20:52:32 +03:00
PRTitle string
PRBody string
Repository string
Owner string
2024-04-02 22:54:15 +03:00
pulls [ ] * giteaPull
2024-03-24 20:52:32 +03:00
}
2024-12-07 02:35:30 +03:00
func NewGitea ( cfg configcli . Config , log logger . Logger ) * Gitea {
2024-03-24 20:52:32 +03:00
return & Gitea {
2024-12-07 02:35:30 +03:00
logger : log ,
2024-03-24 20:52:32 +03:00
URL : cfg . Source . APIURL ,
2024-04-06 15:18:43 +03:00
Username : cfg . Source . Username ,
Password : cfg . Source . Password ,
2024-03-24 20:52:32 +03:00
PRTitle : cfg . PullRequestTitle ,
PRBody : cfg . PullRequestBody ,
Repository : cfg . Source . Repository ,
Owner : cfg . Source . Owner ,
}
}
type giteaPull 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" `
}
2024-04-02 22:54:15 +03:00
func ( g * Gitea ) Name ( ) string {
return "gitea"
}
2024-03-24 20:52:32 +03:00
func ( g * Gitea ) RequestOpen ( ctx context . Context , branch string , path string , mod modules . Update ) error {
2024-12-07 02:35:30 +03:00
g . logger . Debug ( ctx , fmt . Sprintf ( "RequestOpen start, mod title: %s" , path ) )
2024-03-24 20:52:32 +03:00
var buf [ ] byte
var err error
// создания шаблона названия для пулл реквеста
tplTitle , err := template . New ( "pull_request_title" ) . Parse ( g . PRTitle )
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Fatal ( ctx , fmt . Sprintf ( "failed to parse template: %v" , err ) )
2024-03-24 20:52:32 +03:00
}
wTitle := bytes . NewBuffer ( nil )
// создания шаблона тела для пулл реквеста
tplBody , err := template . New ( "pull_request_body" ) . Parse ( g . PRTitle )
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Fatal ( ctx , fmt . Sprintf ( "failed to parse template: %v" , err ) )
2024-03-24 20:52:32 +03:00
}
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 {
2024-12-08 14:16:22 +03:00
g . logger . Error ( ctx , "failed to execute template" , err )
return err
2024-03-24 20:52:32 +03:00
}
if err = tplBody . Execute ( wBody , data ) ; err != nil {
2024-12-08 14:16:22 +03:00
g . logger . Error ( ctx , "failed to execute template" , err )
return err
2024-03-24 20:52:32 +03:00
}
// открытие гит репозитория с опцией обхода репозитория для нахождения .git
2024-12-18 18:01:40 +03:00
repo , err := git . PlainOpenWithOptions ( "." , & git . PlainOpenOptions { DetectDotGit : true } )
2024-03-24 20:52:32 +03:00
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Fatal ( ctx , fmt . Sprintf ( "failed to open repo: %v" , err ) )
2024-03-24 20:52:32 +03:00
}
2024-12-18 17:37:04 +03:00
wtree , headRef , err := g . fetchCheckout ( ctx , repo , branch , path , mod )
2024-12-08 14:16:22 +03:00
if err != nil {
2024-12-18 17:37:04 +03:00
g . logger . Error ( ctx , "failed to checkout" , err )
2024-03-24 20:52:32 +03:00
}
2024-12-08 14:16:22 +03:00
defer func ( ) {
_ = g . checkout ( wtree , headRef )
} ( )
2024-12-18 17:37:04 +03:00
if err = g . scopeUpdateDep ( ctx , path , mod ) ; err != nil {
2024-12-08 14:16:22 +03:00
return err
2024-03-24 20:52:32 +03:00
}
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , "worktree add go.mod" )
2024-12-08 14:16:22 +03:00
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
2024-03-24 20:52:32 +03:00
}
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , "worktree add go.sum" )
2024-12-08 14:16:22 +03:00
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
2024-03-24 20:52:32 +03:00
}
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , "worktree commit" )
2024-03-24 20:52:32 +03:00
_ , 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 {
2024-12-08 14:16:22 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "failed to commit: %v" , err ) )
return err
2024-03-24 20:52:32 +03:00
}
2024-12-07 02:35:30 +03:00
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 как будто нужно переделать
2024-03-24 20:52:32 +03:00
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , fmt . Sprintf ( "try to push refspec %s" , refspec ) )
2024-03-24 20:52:32 +03:00
if err = repo . PushContext ( ctx , & git . PushOptions {
RefSpecs : [ ] gitconfig . RefSpec { refspec } ,
2024-04-03 17:29:57 +03:00
Auth : & httpauth . BasicAuth { Username : g . Username , Password : g . Password } ,
2024-03-24 20:52:32 +03:00
Force : true ,
} ) ; err != nil {
2024-12-08 14:16:22 +03:00
g . logger . Error ( ctx , "failed to push repo branch" , err )
return err
2024-03-24 20:52:32 +03:00
} // пытаемся за пушить изменения
2024-12-18 17:37:04 +03:00
rsp , err := g . postPullRequest ( ctx , wBody , wTitle , branch , path , mod )
2024-03-24 20:52:32 +03:00
if err != nil {
return err
}
2024-12-18 17:37:04 +03:00
// Вроде создаем новый реквест на создание пулл реквеста
2024-03-24 20:52:32 +03:00
if rsp . StatusCode != http . StatusCreated {
buf , _ = io . ReadAll ( rsp . Body )
return fmt . Errorf ( "unknown error: %s" , buf )
}
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , fmt . Sprintf ( "PR create for %s-%s" , path , mod . Version ) )
2024-03-24 20:52:32 +03:00
return nil
}
func ( g * Gitea ) RequestClose ( ctx context . Context , branch string , path string ) error {
2024-12-07 02:35:30 +03:00
g . logger . Debug ( ctx , fmt . Sprintf ( "RequestClose start, mod title: %s" , path ) )
2024-04-21 16:26:11 +03:00
var err error
2024-03-24 20:52:32 +03:00
2024-12-07 02:35:30 +03:00
g . pulls , err = g . GetPulls ( ctx , g . URL , g . Owner , g . Repository , g . Password )
2024-03-24 20:52:32 +03:00
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "GetPulls error: %s" , err ) )
2024-03-24 20:52:32 +03:00
return err
}
prExist := false
var b string // Name of the branch to be deleted
2024-04-21 16:26:11 +03:00
for _ , pull := range g . pulls {
2024-03-24 20:52:32 +03:00
if strings . Contains ( pull . Title , path ) && pull . Base . Ref == branch {
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , fmt . Sprintf ( "PR for %s exists: %s" , path , pull . URL ) )
2024-03-24 20:52:32 +03:00
prExist = true
b = pull . Head . Ref
}
}
if ! prExist {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "skip %s since pr does not exist" , path ) )
2024-03-24 20:52:32 +03:00
return ErrPRNotExist
}
2024-12-07 02:35:30 +03:00
req , err := g . DeleteBranch ( ctx , g . URL , g . Owner , g . Repository , b , g . Password )
2024-03-24 20:52:32 +03:00
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "failed to create request for delete the branch: %s, err: %s" , branch , err ) )
2024-03-24 20:52:32 +03:00
return err
}
rsp , err := http . DefaultClient . Do ( req )
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "failed to do request for delete the branch: %s, err: %s, code: %v" , branch , err , rsp . StatusCode ) )
2024-03-24 20:52:32 +03:00
return err
}
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , fmt . Sprintf ( "Delete branch for %s successful" , path ) )
2024-03-24 20:52:32 +03:00
return nil
}
func ( g * Gitea ) RequestUpdate ( ctx context . Context , branch string , path string , mod modules . Update ) error {
2024-12-07 02:35:30 +03:00
g . logger . Debug ( ctx , fmt . Sprintf ( "RequestUpdate start, mod title: %s" , path ) )
2024-04-02 22:54:15 +03:00
var err error
2024-03-24 20:52:32 +03:00
2024-12-07 02:35:30 +03:00
g . pulls , err = g . GetPulls ( ctx , g . URL , g . Owner , g . Repository , g . Password )
2024-04-21 16:26:11 +03:00
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "GetPulls error: %s" , err ) )
2024-04-21 16:26:11 +03:00
return err
2024-03-24 20:52:32 +03:00
}
prExist := false
2024-12-18 17:37:04 +03:00
var pullId int64
2024-12-18 20:15:16 +03:00
var targetBranch plumbing . ReferenceName
2024-12-18 18:21:40 +03:00
2024-04-02 22:54:15 +03:00
for _ , pull := range g . pulls {
2024-03-24 20:52:32 +03:00
if strings . Contains ( pull . Title , path ) && pull . Base . Ref == branch {
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , fmt . Sprintf ( "don't skip %s since pr exist %s" , path , pull . URL ) ) // todo
tVersion := getVersions ( pull . Head . Ref ) // Надо взять просто из названия ветки последнюю версию
2024-12-18 17:37:04 +03:00
if ! modules . IsNewerVersion ( tVersion , mod . Version , false ) {
2024-12-07 02:35:30 +03:00
g . logger . Debug ( ctx , "The existing PR is relevant" )
2024-03-24 20:52:32 +03:00
return nil
}
prExist = true
2024-12-18 17:37:04 +03:00
pullId = pull . ID
2024-12-18 20:15:16 +03:00
targetBranch = plumbing . ReferenceName ( pull . Head . Ref )
2024-03-24 20:52:32 +03:00
}
}
if ! prExist {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "skip %s since pr does not exist" , path ) )
2024-03-24 20:52:32 +03:00
return ErrPRNotExist
}
2024-12-18 17:37:04 +03:00
// создания шаблона названия для пулл реквеста
tplTitle , err := template . New ( "pull_request_title" ) . Parse ( g . PRTitle )
if err != nil {
g . 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 {
g . 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 {
g . logger . Error ( ctx , "failed to execute template" , err )
return err
}
if err = tplBody . Execute ( wBody , data ) ; err != nil {
g . logger . Error ( ctx , "failed to execute template" , err )
return err
}
// открытие гит репозитория с опцией обхода репозитория для нахождения .git
2024-12-18 18:01:40 +03:00
repo , err := git . PlainOpenWithOptions ( "." , & git . PlainOpenOptions { DetectDotGit : true } )
2024-12-18 17:37:04 +03:00
if err != nil {
g . logger . Fatal ( ctx , fmt . Sprintf ( "failed to open repo: %v" , err ) )
}
2024-12-18 20:15:16 +03:00
wtree , headRef , err := g . fetchCheckout ( ctx , repo , targetBranch . Short ( ) , path , mod )
2024-12-18 17:37:04 +03:00
if err != nil {
g . logger . Error ( ctx , "failed to checkout" , err )
2024-12-18 20:03:08 +03:00
return err
2024-12-18 17:37:04 +03:00
}
defer func ( ) {
_ = g . checkout ( wtree , headRef )
} ( )
if err = g . scopeUpdateDep ( ctx , path , mod ) ; err != nil {
return err
}
g . logger . Info ( ctx , "worktree add go.mod" )
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 . 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" )
_ , 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 {
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 как будто нужно переделать
g . logger . Info ( ctx , fmt . Sprintf ( "try to push refspec %s" , refspec ) )
if err = repo . PushContext ( ctx , & git . PushOptions {
2024-12-18 18:21:40 +03:00
//RefSpecs: []gitconfig.RefSpec{refspec},
Auth : & httpauth . BasicAuth { Username : g . Username , Password : g . Password } ,
Force : true ,
2024-12-18 17:37:04 +03:00
} ) ; err != nil {
g . logger . Error ( ctx , "failed to push repo branch" , err )
return err
} // пытаемся за пушить изменения
err = g . patchPullRequest ( ctx , wBody , wTitle , pullId )
if err != nil {
return err
}
g . logger . Info ( ctx , fmt . Sprintf ( "PR update for %s-%s" , path , mod . Version ) )
return nil
2024-03-24 20:52:32 +03:00
}
func ( g * Gitea ) RequestList ( ctx context . Context , branch string ) ( map [ string ] string , error ) {
2024-12-07 02:35:30 +03:00
g . logger . Debug ( ctx , fmt . Sprintf ( "RequestList for %s" , branch ) )
2024-04-03 08:28:16 +03:00
var err error
2024-03-24 20:52:32 +03:00
2024-12-07 02:35:30 +03:00
g . pulls , err = g . GetPulls ( ctx , g . URL , g . Owner , g . Repository , g . Password )
2024-03-24 20:52:32 +03:00
if err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "GetPulls error: %s" , err ) )
2024-03-24 20:52:32 +03:00
return nil , err
}
var path string
rMap := make ( map [ string ] string )
2024-04-03 08:28:16 +03:00
for _ , pull := range g . pulls {
2024-12-07 02:35:30 +03:00
if ! strings . HasPrefix ( pull . Title , "Bump " ) || pull . Base . Ref != branch { // добавляем только реквесты бота по обновлению модулей
2024-04-02 22:54:15 +03:00
continue
}
2024-12-07 02:35:30 +03:00
path = strings . Split ( pull . Title , " " ) [ 1 ] // todo Работет только для дефолтного шаблона
2024-03-24 20:52:32 +03:00
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
}
2024-12-07 02:35:30 +03:00
func ( g * Gitea ) DeleteBranch ( ctx context . Context , url , owner , repo , branch , password string ) ( * http . Request , error ) {
2024-03-24 20:52:32 +03:00
var buf [ ] byte
2024-04-03 17:29:57 +03:00
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 ) )
2024-03-24 20:52:32 +03:00
if err != nil {
return nil , err
}
req . Header . Add ( "Accept" , "application/json" )
req . Header . Add ( "Content-Type" , "application/json" )
2024-04-21 16:26:11 +03:00
req . Header . Add ( "Authorization" , "Bearer " + password )
2024-03-24 20:52:32 +03:00
return req , err
}
2024-12-07 02:35:30 +03:00
func ( g * Gitea ) GetPulls ( ctx context . Context , url , owner , repo , password string ) ( [ ] * giteaPull , error ) {
2024-04-03 08:28:16 +03:00
var pullsAll [ ] * giteaPull
2024-04-02 22:54:15 +03:00
page := 1
2024-03-24 20:52:32 +03:00
2024-04-02 22:54:15 +03:00
for {
2024-04-03 08:28:16 +03:00
pulls := make ( [ ] * giteaPull , 0 , 10 )
2024-04-02 22:54:15 +03:00
req , err := http . NewRequestWithContext (
ctx ,
http . MethodGet ,
2024-04-06 15:18:43 +03:00
fmt . Sprintf ( "https://%s/api/v1/repos/%s/%s/pulls?state=open&page=%v" , url , owner , repo , page ) ,
2024-04-02 22:54:15 +03:00
nil )
if err != nil {
return nil , err
2024-12-07 02:35:30 +03:00
} // вроде запроса к репозиторию
2024-03-24 20:52:32 +03:00
2024-04-02 22:54:15 +03:00
req . Header . Add ( "Accept" , "application/json" )
req . Header . Add ( "Content-Type" , "application/json" )
2024-04-21 16:26:11 +03:00
req . Header . Add ( "Authorization" , "Bearer " + password )
2024-03-24 20:52:32 +03:00
2024-04-02 22:54:15 +03:00
rsp , err := http . DefaultClient . Do ( req ) // выполнение запроса
if err != nil {
return nil , err
}
2024-03-24 20:52:32 +03:00
2024-04-02 22:54:15 +03:00
buf , _ := io . ReadAll ( rsp . Body )
2024-03-24 20:52:32 +03:00
2024-04-02 22:54:15 +03:00
switch rsp . StatusCode {
case http . StatusOK :
if err = json . Unmarshal ( buf , & pulls ) ; err != nil {
2024-12-07 02:35:30 +03:00
g . logger . Error ( ctx , fmt . Sprintf ( "failed to decode response %s err: %v" , buf , err ) )
2024-04-02 22:54:15 +03:00
return nil , err
}
pullsAll = append ( pullsAll , pulls ... )
page ++
case http . StatusNotFound :
2024-12-07 02:35:30 +03:00
g . logger . Info ( ctx , fmt . Sprintf ( "pull-request is not exist for %s" , repo ) )
2024-04-02 22:54:15 +03:00
return nil , ErrPRNotExist
default :
return nil , fmt . Errorf ( "unknown error: %s" , buf )
}
if len ( pulls ) == 0 {
break
2024-03-24 20:52:32 +03:00
}
}
2024-04-02 22:54:15 +03:00
return pullsAll , nil
2024-03-24 20:52:32 +03:00
}
2024-04-06 15:18:43 +03:00
2024-12-18 18:01:40 +03:00
func ( g * Gitea ) checkout ( w * git . Worktree , ref * plumbing . Reference ) error {
2024-04-06 15:18:43 +03:00
ctx := context . Background ( )
2024-12-18 19:55:07 +03:00
g . logger . Debug ( ctx , "checkout: " + ref . String ( ) )
2024-04-21 16:26:11 +03:00
if err := w . Checkout ( & git . CheckoutOptions {
Branch : ref . Name ( ) ,
Create : false ,
Force : true ,
Keep : false ,
} ) ; err != nil {
2024-12-08 14:16:22 +03:00
g . logger . Error ( ctx , "failed to reset" , err )
return err
2024-04-06 15:18:43 +03:00
}
2024-12-08 14:16:22 +03:00
return nil
2024-04-06 15:18:43 +03:00
}
2024-12-18 17:37:04 +03:00
2024-12-18 18:01:40 +03:00
func ( g Gitea ) fetchCheckout ( ctx context . Context , repo * git . Repository , branch , path string , mod modules . Update ) ( * git . Worktree , * plumbing . Reference , error ) {
2024-12-18 17:37:04 +03:00
// обновляем ветки
if err := repo . FetchContext ( ctx , & git . FetchOptions {
Auth : & httpauth . BasicAuth { Username : g . Username , Password : g . Password } ,
Force : true ,
} ) ; err != nil && err != git . NoErrAlreadyUpToDate {
g . logger . Error ( ctx , "failed to fetch repo" , err )
return nil , nil , err
}
var headRef * plumbing . Reference
branches , err := repo . Branches ( )
if err != nil {
2024-12-18 20:35:55 +03:00
g . logger . Error ( ctx , "cant get repo branch" , err )
2024-12-18 17:37:04 +03:00
return nil , nil , err
}
2024-12-18 18:01:40 +03:00
for {
ref , err := branches . Next ( )
if err != nil {
return nil , nil , err
}
2024-12-18 17:37:04 +03:00
if ref . Name ( ) . Short ( ) == branch {
//Получаем ссылку на нужную ветку
2024-12-18 20:18:19 +03:00
headRef = ref
2024-12-18 17:37:04 +03:00
g . logger . Info ( ctx , "headRef set to " + headRef . String ( ) )
break
}
}
if headRef == nil {
g . logger . Error ( ctx , "failed to get repo branch head" )
return nil , nil , err
} // Н е получили нужную ветку
g . logger . Info ( ctx , "repo head " + headRef . String ( ) )
wtree , err := repo . Worktree ( )
if err != nil {
g . logger . Error ( ctx , "failed to get worktree" , err )
return nil , nil , err
}
2024-12-18 20:35:55 +03:00
if err = wtree . Reset ( & git . ResetOptions {
2024-12-18 20:53:05 +03:00
Mode : git . HardReset ,
2024-12-18 20:35:55 +03:00
} ) ; err != nil {
g . logger . Error ( ctx , "reset work_tree error: " , err )
return nil , nil , err
}
2024-12-18 17:37:04 +03:00
g . pulls , err = g . GetPulls ( ctx , g . URL , g . Owner , g . Repository , g . Password )
if err != nil && err != ErrPRNotExist {
g . logger . Error ( ctx , "GetPulls error" , err )
return nil , nil , err
}
var pullExist bool
for _ , pull := range g . pulls {
2024-12-18 18:21:40 +03:00
if strings . Contains ( pull . Title , path ) && ( strings . Contains ( pull . Base . Ref , branch ) || strings . Contains ( pull . Head . Ref , branch ) ) {
2024-12-18 17:37:04 +03:00
pullExist = true
} // хотим проверить есть ли пулл реквест для этой ветки, если есть то выходим
}
g . logger . Info ( ctx , fmt . Sprintf ( "update %s from %s to %s" , path , mod . Module . Version , mod . Version ) )
wstatus , err := wtree . Status ( )
if err != nil {
g . logger . Error ( ctx , "failed to get worktree status" , err )
return nil , nil , err
}
g . logger . Info ( ctx , "worktree status " + wstatus . String ( ) )
if err = wtree . PullContext ( ctx , & git . PullOptions {
Auth : & httpauth . BasicAuth { Username : g . Username , Password : g . Password } ,
// Depth: 1,
// RemoteURL :
2024-12-18 20:18:19 +03:00
ReferenceName : headRef . Name ( ) ,
2024-12-18 20:15:16 +03:00
Force : true ,
RemoteName : "origin" ,
2024-12-18 17:37:04 +03:00
} ) ; err != nil && err != git . NoErrAlreadyUpToDate {
g . logger . Error ( ctx , fmt . Sprintf ( "failed to pull repo: %v" , err ) ) // подтягиваем изменения с удаленого репозитория
return nil , nil , err
}
g . logger . Info ( ctx , fmt . Sprintf ( "checkout ref %s" , headRef ) )
if pullExist {
if err = wtree . Checkout ( & git . CheckoutOptions {
2024-12-18 20:18:54 +03:00
Branch : headRef . Name ( ) ,
2024-12-18 17:37:04 +03:00
Create : false ,
Force : true ,
} ) ; err != nil && err != git . ErrBranchExists {
g . logger . Error ( ctx , fmt . Sprintf ( "failed to checkout tree: %v" , err ) )
return nil , nil , err
} //переходим на существующею
} else {
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 {
g . logger . Error ( ctx , fmt . Sprintf ( "failed to checkout tree: %v" , err ) )
return nil , nil , err
} // создаем новую ветку
}
return wtree , headRef , nil
}
func ( g * Gitea ) postPullRequest ( ctx context . Context , wBody , wTitle * bytes . Buffer , branch , path string , mod modules . Update ) ( * http . Response , error ) {
body := map [ string ] string {
"base" : branch ,
"body" : wBody . String ( ) ,
"head" : fmt . Sprintf ( "pkgdash/go_modules/%s-%s" , path , mod . Version ) ,
"title" : wTitle . String ( ) ,
}
g . logger . Info ( ctx , fmt . Sprintf ( "raw body: %#+v" , body ) )
buf , err := json . Marshal ( body )
if err != nil {
g . logger . Error ( ctx , "failed to marshal" , err )
return nil , err
}
g . 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 {
g . logger . Error ( ctx , "http request error" , err )
return nil , err
}
req . Header . Add ( "Accept" , "application/json" )
req . Header . Add ( "Content-Type" , "application/json" )
req . Header . Add ( "Authorization" , "Bearer " + g . Password )
rsp , err := http . DefaultClient . Do ( req )
if err != nil {
g . logger . Error ( ctx , "failed to call http request" , err )
return rsp , err
}
return rsp , nil
}
func ( g * Gitea ) patchPullRequest ( ctx context . Context , wBody , wTitle * bytes . Buffer , indexPR int64 ) error {
body := map [ string ] string {
"body" : wBody . String ( ) ,
"title" : wTitle . String ( ) ,
}
g . logger . Info ( ctx , fmt . Sprintf ( "raw body: %#+v" , body ) )
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 ,
2024-12-18 18:33:51 +03:00
http . MethodPatch ,
2024-12-18 17:37:04 +03:00
fmt . Sprintf ( "https://%s/api/v1/repos/%s/%s/pulls/%d" , g . URL , g . Owner , g . Repository , indexPR ) ,
bytes . NewReader ( buf ) ,
)
if err != nil {
g . logger . Error ( ctx , "http request error" , err )
return err
}
req . Header . Add ( "Accept" , "application/json" )
req . Header . Add ( "Content-Type" , "application/json" )
req . Header . Add ( "Authorization" , "Bearer " + g . Password )
_ , err = http . DefaultClient . Do ( req )
if err != nil {
g . logger . Error ( ctx , "failed to call http request" , err )
return err
}
return nil
}
func ( g * Gitea ) scopeUpdateDep ( ctx context . Context , path string , mod modules . Update ) error {
epath , err := exec . LookPath ( "go" )
if errors . Is ( err , exec . ErrDot ) {
err = nil
}
if err != nil {
g . 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 {
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 . 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 . Error ( ctx , fmt . Sprintf ( "failed to run go mod tidy: %s err: %v" , out , err ) )
return err
} // пытаемся выполнить команду go mod tidy пытаемся подтянуть новую версию модуля
return nil
}