wip
This commit is contained in:
@@ -18,7 +18,7 @@ type BasicPR struct {
|
|||||||
Num int64
|
Num int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var validOrgAndRepoRx *regexp.Regexp = regexp.MustCompile("^[A-Za-z0-9_-]+$")
|
var validOrgAndRepoRx *regexp.Regexp = regexp.MustCompile("^[A-Za-z0-9_\\.-]+$")
|
||||||
|
|
||||||
func parsePrLine(line string) (BasicPR, error) {
|
func parsePrLine(line string) (BasicPR, error) {
|
||||||
var ret BasicPR
|
var ret BasicPR
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ type GiteaFileContentAndRepoFetcher interface {
|
|||||||
GiteaRepoFetcher
|
GiteaRepoFetcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func PartiallyParseWorkflowConfig(data []byte) (*AutogitConfig, error) {
|
func UnmarshalWorkflowConfig(data []byte) (*AutogitConfig, error) {
|
||||||
var config AutogitConfig
|
var config AutogitConfig
|
||||||
data, err := hujson.Standardize(data)
|
data, err := hujson.Standardize(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -130,7 +130,7 @@ func ReadWorkflowConfig(gitea GiteaFileContentAndRepoFetcher, git_project string
|
|||||||
return nil, fmt.Errorf("Error fetching 'workflow.config' for %s/%s#%s: %w", a[0], prjGitRepo, branch, err)
|
return nil, fmt.Errorf("Error fetching 'workflow.config' for %s/%s#%s: %w", a[0], prjGitRepo, branch, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := PartiallyParseWorkflowConfig(data)
|
config, err := UnmarshalWorkflowConfig(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,19 +196,26 @@ func (refs *GitReferences) addReference(id, branch string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error) {
|
func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error) {
|
||||||
|
LogDebug("Cloning", remoteUrl, " repo:", repo, " branch:", branch)
|
||||||
remoteUrlComp, err := ParseGitRemoteUrl(remoteUrl)
|
remoteUrlComp, err := ParseGitRemoteUrl(remoteUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Cannot parse remote URL: %w", err)
|
return "", fmt.Errorf("Cannot parse remote URL: %w", err)
|
||||||
}
|
}
|
||||||
if len(branch) == 0 {
|
remoteBranch := "HEAD"
|
||||||
|
if len(branch) == 0 && remoteUrlComp != nil {
|
||||||
branch = remoteUrlComp.Commit
|
branch = remoteUrlComp.Commit
|
||||||
}
|
remoteBranch = branch
|
||||||
if len(branch) == 0 {
|
} else if len(branch) > 0 {
|
||||||
branch = "HEAD"
|
remoteBranch = branch
|
||||||
}
|
}
|
||||||
remoteName := remoteUrlComp.RemoteName()
|
remoteName := remoteUrlComp.RemoteName()
|
||||||
|
if remoteUrlComp != nil {
|
||||||
LogDebug("Clone", *remoteUrlComp, " -> ", remoteName)
|
LogDebug("Clone", *remoteUrlComp, " -> ", remoteName)
|
||||||
|
} else {
|
||||||
|
LogDebug("Clone", "[default] -> ", remoteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteRef := remoteName + "/" + remoteBranch
|
||||||
if fi, err := os.Stat(path.Join(e.GitPath, repo)); os.IsNotExist(err) {
|
if fi, err := os.Stat(path.Join(e.GitPath, repo)); os.IsNotExist(err) {
|
||||||
if err = e.GitExec("", "clone", "--origin", remoteName, remoteUrl, repo); err != nil {
|
if err = e.GitExec("", "clone", "--origin", remoteName, remoteUrl, repo); err != nil {
|
||||||
return remoteName, err
|
return remoteName, err
|
||||||
@@ -222,9 +229,34 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
|
|||||||
e.GitExecOrPanic(repo, "remote", "set-url", remoteName, remoteUrl)
|
e.GitExecOrPanic(repo, "remote", "set-url", remoteName, remoteUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.GitExecOrPanic(repo, "fetch", remoteName, branch)
|
// check if we have submodule to deinit
|
||||||
|
if list, _ := e.GitSubmoduleList(repo, "HEAD"); len(list) > 0 {
|
||||||
|
e.GitExecOrPanic(repo, "submodule", "deinit", "--all", "--force")
|
||||||
}
|
}
|
||||||
return remoteName, e.GitExec(repo, "checkout", "-B", branch, "refs/remotes/"+remoteName+"/"+branch)
|
|
||||||
|
e.GitExecOrPanic(repo, "fetch", remoteName, remoteBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
refsBytes, err := os.ReadFile(path.Join(e.GitPath, repo, ".git/refs/remotes", remoteName, "HEAD"))
|
||||||
|
if err != nil {
|
||||||
|
LogError("Cannot read HEAD of remote", remoteName)
|
||||||
|
return remoteName, fmt.Errorf("Cannot read HEAD of remote %s", remoteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
refs := string(refsBytes)
|
||||||
|
if refs[0:5] != "ref: " {
|
||||||
|
LogError("Unexpected format of remote HEAD ref:", refs)
|
||||||
|
return remoteName, fmt.Errorf("Unexpected format of remote HEAD ref: %s", refs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(branch) == 0 || branch == "HEAD" {
|
||||||
|
remoteRef = strings.TrimSpace(refs[5:])
|
||||||
|
branch = remoteRef[strings.LastIndex(remoteRef, "/")+1:]
|
||||||
|
LogDebug("remoteRef", remoteRef)
|
||||||
|
LogDebug("branch", branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteName, e.GitExec(repo, "checkout", "--track", "-B", branch, remoteRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error) {
|
func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error) {
|
||||||
@@ -306,7 +338,7 @@ func (e *GitHandlerImpl) GitExecWithOutput(cwd string, params ...string) (string
|
|||||||
cmd.Dir = filepath.Join(e.GitPath, cwd)
|
cmd.Dir = filepath.Join(e.GitPath, cwd)
|
||||||
cmd.Stdin = nil
|
cmd.Stdin = nil
|
||||||
|
|
||||||
LogDebug("git execute:", cmd.Args)
|
LogDebug("git execute @", cwd, ":", cmd.Args)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
LogDebug(string(out))
|
LogDebug(string(out))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ func TestGitClone(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
execPath, err := os.Getwd()
|
execPath, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -239,6 +239,9 @@ func (gitea *GiteaTransport) SetCommitStatus(org, repo, hash string, status *mod
|
|||||||
}),
|
}),
|
||||||
gitea.transport.DefaultAuthentication,
|
gitea.transport.DefaultAuthentication,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return res.Payload, err
|
return res.Payload, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,6 +488,9 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
|
|||||||
func (gitea *GiteaTransport) GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, refOrg, refRepo string, Index int64) (*models.PullRequest, error) {
|
func (gitea *GiteaTransport) GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, refOrg, refRepo string, Index int64) (*models.PullRequest, error) {
|
||||||
var page int64
|
var page int64
|
||||||
state := "open"
|
state := "open"
|
||||||
|
|
||||||
|
prLine := fmt.Sprintf(PrPattern, refOrg, refRepo, Index)
|
||||||
|
LogDebug("Finding PrjGitPR for", prLine, " Looking in", prjGitOrg, "/", prjGitRepo)
|
||||||
for {
|
for {
|
||||||
page++
|
page++
|
||||||
prs, err := gitea.client.Repository.RepoListPullRequests(
|
prs, err := gitea.client.Repository.RepoListPullRequests(
|
||||||
@@ -501,14 +507,13 @@ func (gitea *GiteaTransport) GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, refOrg
|
|||||||
return nil, fmt.Errorf("cannot fetch PR list for %s / %s : %w", prjGitOrg, prjGitRepo, err)
|
return nil, fmt.Errorf("cannot fetch PR list for %s / %s : %w", prjGitOrg, prjGitRepo, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
prLine := fmt.Sprintf(PrPattern, refOrg, refRepo, Index)
|
|
||||||
|
|
||||||
// payload_processing:
|
// payload_processing:
|
||||||
for _, pr := range prs.Payload {
|
for _, pr := range prs.Payload {
|
||||||
lines := strings.Split(pr.Body, "\n")
|
lines := strings.Split(pr.Body, "\n")
|
||||||
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
if strings.TrimSpace(line) == prLine {
|
if strings.TrimSpace(line) == prLine {
|
||||||
|
LogDebug("Found PR:", pr.Index)
|
||||||
return pr, nil
|
return pr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,7 +674,7 @@ endPrs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if pr is closed for more than a week, assume that we are done too
|
// if pr is closed for more than a week, assume that we are done too
|
||||||
if time.Since(time.Time(pr.Updated)) > 7*24*time.Hour {
|
if pr.State == "closed" && time.Since(time.Time(pr.Updated)) > 7*24*time.Hour {
|
||||||
break endPrs
|
break endPrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ func PanicOnError(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PanicOnErrorWithMsg(err error, msg string) {
|
func PanicOnErrorWithMsg(err error, msg ...any) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(msg)
|
LogError(msg...)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,12 +44,13 @@ func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, org, prjGit
|
|||||||
dir := true
|
dir := true
|
||||||
if err != nil || data == nil {
|
if err != nil || data == nil {
|
||||||
dir = false
|
dir = false
|
||||||
if _, notFound := err.(*repository.RepoGetRawFileNotFound); !notFound {
|
if _, notFound := err.(*repository.RepoGetContentsNotFound); !notFound {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
LogDebug("Falling back to maintainership file")
|
||||||
data, _, err = gitea.FetchMaintainershipFile(org, prjGit, branch)
|
data, _, err = gitea.FetchMaintainershipFile(org, prjGit, branch)
|
||||||
if err != nil || data == nil {
|
if err != nil || data == nil {
|
||||||
if _, notFound := err.(*repository.RepoGetRawFileNotFound); !notFound {
|
if _, notFound := err.(*repository.RepoGetContentsNotFound); !notFound {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ type ObsClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewObsClient(host string) (*ObsClient, error) {
|
func NewObsClient(host string) (*ObsClient, error) {
|
||||||
baseUrl, err := url.Parse("https://" + host)
|
baseUrl, err := url.Parse(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -186,6 +186,7 @@ func (c *ObsClient) GetGroupMeta(gid string) (*GroupMeta, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
var meta GroupMeta
|
var meta GroupMeta
|
||||||
err = xml.Unmarshal(data, &meta)
|
err = xml.Unmarshal(data, &meta)
|
||||||
@@ -215,6 +216,7 @@ func (c *ObsClient) GetUserMeta(uid string) (*UserMeta, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
var meta UserMeta
|
var meta UserMeta
|
||||||
err = xml.Unmarshal(data, &meta)
|
err = xml.Unmarshal(data, &meta)
|
||||||
@@ -238,6 +240,11 @@ func (c *ObsClient) ObsRequest(method string, url string, body io.Reader) (*http
|
|||||||
req.Header.Add("cookie", c.cookie)
|
req.Header.Add("cookie", c.cookie)
|
||||||
}
|
}
|
||||||
res, err := c.client.Do(req)
|
res, err := c.client.Do(req)
|
||||||
|
if err != nil && res == nil {
|
||||||
|
LogDebug(err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
if err == nil && res.StatusCode == 200 {
|
if err == nil && res.StatusCode == 200 {
|
||||||
auth_cookie := res.Header.Get("set-cookie")
|
auth_cookie := res.Header.Get("set-cookie")
|
||||||
if auth_cookie != "" {
|
if auth_cookie != "" {
|
||||||
@@ -331,6 +338,7 @@ func (c *ObsClient) GetProjectMeta(project string) (*ProjectMeta, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
return parseProjectMeta(data)
|
return parseProjectMeta(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,6 +362,7 @@ func (c *ObsClient) GetPackageMeta(project, pkg string) (*PackageMeta, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
var meta PackageMeta
|
var meta PackageMeta
|
||||||
err = xml.Unmarshal(data, &meta)
|
err = xml.Unmarshal(data, &meta)
|
||||||
@@ -412,6 +421,7 @@ func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 200:
|
case 200:
|
||||||
@@ -428,6 +438,7 @@ func (c *ObsClient) DeleteProject(project string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return fmt.Errorf("Unexpected return code: %d", res.StatusCode)
|
return fmt.Errorf("Unexpected return code: %d", res.StatusCode)
|
||||||
@@ -494,7 +505,7 @@ func (r *BuildResultList) GetPackageList() []string {
|
|||||||
|
|
||||||
func (r *BuildResultList) BuildResultSummary() (success, finished bool) {
|
func (r *BuildResultList) BuildResultSummary() (success, finished bool) {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return true, true
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
finished = len(r.Result) > 0 && len(r.Result[0].Status) > 0
|
finished = len(r.Result) > 0 && len(r.Result[0].Status) > 0
|
||||||
@@ -698,6 +709,7 @@ func (c *ObsClient) ProjectConfig(project string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data, err := io.ReadAll(res.Body); err == nil {
|
if data, err := io.ReadAll(res.Body); err == nil {
|
||||||
|
defer res.Body.Close()
|
||||||
return string(data), nil
|
return string(data), nil
|
||||||
} else {
|
} else {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -750,5 +762,6 @@ func (c *ObsClient) BuildStatusWithState(project string, opts *BuildResultOption
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
return parseBuildResults(data)
|
return parseBuildResults(data)
|
||||||
}
|
}
|
||||||
|
|||||||
33
common/pr.go
33
common/pr.go
@@ -15,6 +15,7 @@ import (
|
|||||||
type PRInfo struct {
|
type PRInfo struct {
|
||||||
PR *models.PullRequest
|
PR *models.PullRequest
|
||||||
Reviews *PRReviews
|
Reviews *PRReviews
|
||||||
|
RemoteName string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PRSet struct {
|
type PRSet struct {
|
||||||
@@ -31,7 +32,8 @@ func readPRData(gitea GiteaPRFetcher, pr *models.PullRequest, currentSet []PRInf
|
|||||||
retSet := []PRInfo{PRInfo{PR: pr}}
|
retSet := []PRInfo{PRInfo{PR: pr}}
|
||||||
|
|
||||||
// only need to extact there on PrjGit PR
|
// only need to extact there on PrjGit PR
|
||||||
if pr.Base.Repo.Name == config.GitProjectName && pr.Base.Repo.Owner.UserName == config.Organization {
|
org, repo, _ := config.GetPrjGit()
|
||||||
|
if pr.Base.Repo.Name == repo && pr.Base.Repo.Owner.UserName == org {
|
||||||
_, refPRs := ExtractDescriptionAndPRs(bufio.NewScanner(strings.NewReader(pr.Body)))
|
_, refPRs := ExtractDescriptionAndPRs(bufio.NewScanner(strings.NewReader(pr.Body)))
|
||||||
for _, prdata := range refPRs {
|
for _, prdata := range refPRs {
|
||||||
pr, err := gitea.GetPullRequest(prdata.Org, prdata.Repo, prdata.Num)
|
pr, err := gitea.GetPullRequest(prdata.Org, prdata.Repo, prdata.Num)
|
||||||
@@ -54,7 +56,11 @@ func FetchPRSet(gitea GiteaPRFetcher, org, repo string, num int64, config *Autog
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
|
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
|
||||||
if prjGitOrg != org || prjGitRepo != config.GitProjectName {
|
if prjGitOrg == org && prjGitRepo == repo {
|
||||||
|
if pr, err = gitea.GetPullRequest(org, repo, num); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if pr, err = gitea.GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, org, repo, num); err != nil {
|
if pr, err = gitea.GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, org, repo, num); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -64,10 +70,6 @@ func FetchPRSet(gitea GiteaPRFetcher, org, repo string, num int64, config *Autog
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if pr, err = gitea.GetPullRequest(org, repo, num); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prs, err := readPRData(gitea, pr, nil, config)
|
prs, err := readPRData(gitea, pr, nil, config)
|
||||||
@@ -79,7 +81,8 @@ func FetchPRSet(gitea GiteaPRFetcher, org, repo string, num int64, config *Autog
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rs *PRSet) IsPrjGitPR(pr *models.PullRequest) bool {
|
func (rs *PRSet) IsPrjGitPR(pr *models.PullRequest) bool {
|
||||||
return pr.Base.Repo.Name == rs.Config.GitProjectName && pr.Base.Repo.Owner.UserName == rs.Config.Organization
|
org, repo, _ := rs.Config.GetPrjGit()
|
||||||
|
return pr.Base.Repo.Name == repo && pr.Base.Repo.Owner.UserName == org
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *PRSet) GetPrjGitPR() (*models.PullRequest, error) {
|
func (rs *PRSet) GetPrjGitPR() (*models.PullRequest, error) {
|
||||||
@@ -215,8 +218,9 @@ func (rs *PRSet) Merge(gh GitHandlerGenerator) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
git.GitExecOrPanic("", "clone", "--depth", "1", prjgit.Base.Repo.SSHURL, DefaultGitPrj)
|
remote, err := git.GitClone(DefaultGitPrj, rs.Config.Branch, prjgit.Base.Repo.SSHURL)
|
||||||
git.GitExecOrPanic(DefaultGitPrj, "fetch", "origin", prjgit.Base.Sha, prjgit.Head.Sha)
|
PanicOnError(err)
|
||||||
|
git.GitExecOrPanic(DefaultGitPrj, "fetch", remote, prjgit.Base.Sha, prjgit.Head.Sha)
|
||||||
|
|
||||||
// if other changes merged, check if we have conflicts
|
// if other changes merged, check if we have conflicts
|
||||||
rev := strings.TrimSpace(git.GitExecWithOutputOrPanic(DefaultGitPrj, "merge-base", "HEAD", prjgit.Base.Sha, prjgit.Head.Sha))
|
rev := strings.TrimSpace(git.GitExecWithOutputOrPanic(DefaultGitPrj, "merge-base", "HEAD", prjgit.Base.Sha, prjgit.Head.Sha))
|
||||||
@@ -303,8 +307,6 @@ func (rs *PRSet) Merge(gh GitHandlerGenerator) error {
|
|||||||
return fmt.Errorf("Can't close .gitmodules: %w", err)
|
return fmt.Errorf("Can't close .gitmodules: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.CopyFS("/tmp/test", os.DirFS(git.GetPath()))
|
|
||||||
|
|
||||||
git.GitExecOrPanic(DefaultGitPrj, "add", ".gitmodules")
|
git.GitExecOrPanic(DefaultGitPrj, "add", ".gitmodules")
|
||||||
git.GitExecOrPanic(DefaultGitPrj, "-c", "core.editor=true", "merge", "--continue")
|
git.GitExecOrPanic(DefaultGitPrj, "-c", "core.editor=true", "merge", "--continue")
|
||||||
}
|
}
|
||||||
@@ -316,18 +318,19 @@ func (rs *PRSet) Merge(gh GitHandlerGenerator) error {
|
|||||||
if rs.IsPrjGitPR(prinfo.PR) {
|
if rs.IsPrjGitPR(prinfo.PR) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
git.GitExecOrPanic("", "clone", prinfo.PR.Base.Repo.SSHURL, prinfo.PR.Base.Name)
|
prinfo.RemoteName, err = git.GitClone(prinfo.PR.Base.Name, rs.Config.Branch, prinfo.PR.Base.Repo.SSHURL)
|
||||||
git.GitExecOrPanic(prinfo.PR.Base.Name, "fetch", "origin", prinfo.PR.Head.Sha)
|
PanicOnError(err)
|
||||||
|
git.GitExecOrPanic(prinfo.PR.Base.Name, "fetch", prinfo.RemoteName, prinfo.PR.Head.Sha)
|
||||||
git.GitExecOrPanic(prinfo.PR.Base.Name, "merge", "--ff", prinfo.PR.Head.Sha)
|
git.GitExecOrPanic(prinfo.PR.Base.Name, "merge", "--ff", prinfo.PR.Head.Sha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// push changes
|
// push changes
|
||||||
git.GitExecOrPanic(DefaultGitPrj, "push", "origin")
|
git.GitExecOrPanic(DefaultGitPrj, "push", remote)
|
||||||
for _, prinfo := range rs.PRs {
|
for _, prinfo := range rs.PRs {
|
||||||
if rs.IsPrjGitPR(prinfo.PR) {
|
if rs.IsPrjGitPR(prinfo.PR) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
git.GitExecOrPanic(prinfo.PR.Base.Name, "push", "origin")
|
git.GitExecOrPanic(prinfo.PR.Base.Name, "push", prinfo.RemoteName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -463,13 +463,17 @@ func TestPRAssignReviewers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPRMerge(t *testing.T) {
|
func TestPRMerge(t *testing.T) {
|
||||||
|
repoDir := t.TempDir()
|
||||||
|
|
||||||
cwd, _ := os.Getwd()
|
cwd, _ := os.Getwd()
|
||||||
cmd := exec.Command("/usr/bin/bash", path.Join(cwd, "test_repo_setup.sh"))
|
cmd := exec.Command(path.Join(cwd, "test_repo_setup.sh"))
|
||||||
cmd.Dir = t.TempDir()
|
cmd.Dir = repoDir
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
t.Fatal(string(out))
|
t.Fatal(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common.SetLoggingLevel(common.LogLevelDebug)
|
||||||
|
|
||||||
common.ExtraGitParams = []string{
|
common.ExtraGitParams = []string{
|
||||||
"GIT_CONFIG_COUNT=1",
|
"GIT_CONFIG_COUNT=1",
|
||||||
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
||||||
@@ -504,7 +508,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
Owner: &models.User{
|
Owner: &models.User{
|
||||||
UserName: "org",
|
UserName: "org",
|
||||||
},
|
},
|
||||||
SSHURL: path.Join(cmd.Dir, "prjgit"),
|
SSHURL: "file://" + path.Join(repoDir, "prjgit"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
@@ -524,7 +528,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
Owner: &models.User{
|
Owner: &models.User{
|
||||||
UserName: "org",
|
UserName: "org",
|
||||||
},
|
},
|
||||||
SSHURL: path.Join(cmd.Dir, "prjgit"),
|
SSHURL: "file://" + path.Join(cmd.Dir, "prjgit"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
@@ -539,6 +543,8 @@ func TestPRMerge(t *testing.T) {
|
|||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
mock := mock_common.NewMockGiteaPRFetcher(ctl)
|
mock := mock_common.NewMockGiteaPRFetcher(ctl)
|
||||||
|
|
||||||
|
testDir := t.TempDir()
|
||||||
|
t.Log("dir:", testDir)
|
||||||
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
|
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
|
||||||
|
|
||||||
set, err := common.FetchPRSet(mock, "org", "prj", 1, config)
|
set, err := common.FetchPRSet(mock, "org", "prj", 1, config)
|
||||||
@@ -546,8 +552,12 @@ func TestPRMerge(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gh, _ := common.AllocateGitWorkTree("", "", "")
|
gh, _ := common.AllocateGitWorkTree(testDir, "", "")
|
||||||
if err = set.Merge(gh); err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) {
|
err = set.Merge(gh)
|
||||||
|
|
||||||
|
if err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) {
|
||||||
|
os.CopyFS("/tmp/upstream", os.DirFS(repoDir))
|
||||||
|
os.CopyFS("/tmp/out", os.DirFS(testDir))
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ var valid_schemas []string = []string{"https", "ssh", "http", "file"}
|
|||||||
|
|
||||||
func ParseGitRemoteUrl(urlString string) (*GitUrl, error) {
|
func ParseGitRemoteUrl(urlString string) (*GitUrl, error) {
|
||||||
url, err := url.Parse(urlString)
|
url, err := url.Parse(urlString)
|
||||||
|
|
||||||
|
if url != nil && url.Scheme == "file" && err == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil || !slices.Contains(valid_schemas, url.Scheme) {
|
if err != nil || !slices.Contains(valid_schemas, url.Scheme) {
|
||||||
u, err := TranslateSshNativeToUrl(urlString)
|
u, err := TranslateSshNativeToUrl(urlString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -105,15 +105,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
||||||
if _, finished := project.BuildResultSummary(); !finished {
|
|
||||||
return BuildStatusSummaryBuilding
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, finished := refProject.BuildResultSummary(); !finished {
|
if _, finished := refProject.BuildResultSummary(); !finished {
|
||||||
common.LogDebug("refProject not finished building??")
|
common.LogDebug("refProject not finished building??")
|
||||||
return BuildStatusSummaryUnknown
|
return BuildStatusSummaryUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, finished := project.BuildResultSummary(); !finished {
|
||||||
|
common.LogDebug("Still building...")
|
||||||
|
return BuildStatusSummaryBuilding
|
||||||
|
}
|
||||||
|
|
||||||
// the repositories should be setup equally between the projects. We
|
// the repositories should be setup equally between the projects. We
|
||||||
// need to verify that packages that are building in `refProject` are not
|
// need to verify that packages that are building in `refProject` are not
|
||||||
// failing in the `project`
|
// failing in the `project`
|
||||||
@@ -684,7 +685,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) error {
|
|||||||
TargetURL: ObsWebHost + "/project/show/" + stagingProject,
|
TargetURL: ObsWebHost + "/project/show/" + stagingProject,
|
||||||
}
|
}
|
||||||
if change != RequestModificationNoChange {
|
if change != RequestModificationNoChange {
|
||||||
gitea.SetCommitStatus(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Head.Sha, status)
|
common.LogDebug(gitea.SetCommitStatus(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Head.Sha, status))
|
||||||
}
|
}
|
||||||
|
|
||||||
if change == RequestModificationProjectCreated {
|
if change == RequestModificationProjectCreated {
|
||||||
@@ -808,6 +809,9 @@ func main() {
|
|||||||
ObsWebHost = ObsWebHostFromApiHost(ObsApiHost)
|
ObsWebHost = ObsWebHostFromApiHost(ObsApiHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common.LogDebug("OBS Web Host:", ObsWebHost)
|
||||||
|
common.LogDebug("OBS API Host:", ObsApiHost)
|
||||||
|
|
||||||
common.PanicOnErrorWithMsg(common.RequireGiteaSecretToken(), "Cannot find GITEA_TOKEN")
|
common.PanicOnErrorWithMsg(common.RequireGiteaSecretToken(), "Cannot find GITEA_TOKEN")
|
||||||
common.PanicOnErrorWithMsg(common.RequireObsSecretToken(), "Cannot find OBS_USER and OBS_PASSWORD")
|
common.PanicOnErrorWithMsg(common.RequireObsSecretToken(), "Cannot find OBS_USER and OBS_PASSWORD")
|
||||||
|
|
||||||
@@ -828,9 +832,6 @@ func main() {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
PollWorkNotifications(GiteaUrl)
|
PollWorkNotifications(GiteaUrl)
|
||||||
if *debug {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
common.LogInfo("Poll cycle finished")
|
common.LogInfo("Poll cycle finished")
|
||||||
time.Sleep(5 * time.Minute)
|
time.Sleep(5 * time.Minute)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ func main() {
|
|||||||
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance")
|
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance")
|
||||||
rabbitUrl := flag.String("url", "amqps://rabbit.opensuse.org", "URL for RabbitMQ instance")
|
rabbitUrl := flag.String("url", "amqps://rabbit.opensuse.org", "URL for RabbitMQ instance")
|
||||||
flag.BoolVar(&DebugMode, "debug", false, "Extra debugging information")
|
flag.BoolVar(&DebugMode, "debug", false, "Extra debugging information")
|
||||||
flag.BoolVar(&noop, "no-op", false, "No-op mode. Do not push changes to remote repo.")
|
flag.BoolVar(&noop, "dry", false, "Dry mode. Do not push changes to remote repo.")
|
||||||
flag.BoolVar(&checkOnStart, "check-on-start", false, "Check all repositories for consistency on start, without delays")
|
flag.BoolVar(&checkOnStart, "check-on-start", false, "Check all repositories for consistency on start, without delays")
|
||||||
checkIntervalHours := flag.Float64("check-interval", 5, "Check interval (+-random delay) for repositories for consitency, in hours")
|
checkIntervalHours := flag.Float64("check-interval", 5, "Check interval (+-random delay) for repositories for consitency, in hours")
|
||||||
basePath := flag.String("repo-path", "", "Repository path. Default is temporary directory")
|
basePath := flag.String("repo-path", "", "Repository path. Default is temporary directory")
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
@@ -39,6 +41,7 @@ var ListPROnly bool
|
|||||||
var PRID int64
|
var PRID int64
|
||||||
var CurrentUser *models.User
|
var CurrentUser *models.User
|
||||||
var GitHandler common.GitHandlerGenerator
|
var GitHandler common.GitHandlerGenerator
|
||||||
|
var Gitea common.Gitea
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
workflowConfig := flag.String("config", "", "Repository and workflow definition file")
|
workflowConfig := flag.String("config", "", "Repository and workflow definition file")
|
||||||
@@ -50,6 +53,7 @@ func main() {
|
|||||||
flag.BoolVar(&ListPROnly, "list-prs-only", false, "Only lists PRs without acting on them")
|
flag.BoolVar(&ListPROnly, "list-prs-only", false, "Only lists PRs without acting on them")
|
||||||
flag.Int64Var(&PRID, "id", -1, "Process only the specific ID and ignore the rest. Use for debugging")
|
flag.Int64Var(&PRID, "id", -1, "Process only the specific ID and ignore the rest. Use for debugging")
|
||||||
basePath := flag.String("repo-path", "", "Repository path. Default is temporary directory")
|
basePath := flag.String("repo-path", "", "Repository path. Default is temporary directory")
|
||||||
|
pr := flag.String("only-pr", "", "Only specific PR to process. For debugging")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
common.SetLoggingLevel(common.LogLevelInfo)
|
common.SetLoggingLevel(common.LogLevelInfo)
|
||||||
@@ -72,19 +76,23 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea := common.AllocateGiteaTransport(*giteaUrl)
|
Gitea = common.AllocateGiteaTransport(*giteaUrl)
|
||||||
config, err := common.ReadConfigFile(*workflowConfig)
|
config, err := common.ReadConfigFile(*workflowConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot read config files:", err)
|
common.LogError("Cannot read config files:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configs, err := common.ResolveWorkflowConfigs(gitea, config)
|
configs, err := common.ResolveWorkflowConfigs(Gitea, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot resolve config files:", err)
|
common.LogError("Cannot resolve config files:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, c := range configs {
|
||||||
|
common.LogDebug(*c)
|
||||||
|
}
|
||||||
|
|
||||||
req := new(RequestProcessor)
|
req := new(RequestProcessor)
|
||||||
req.configuredRepos = make(map[string][]*common.AutogitConfig)
|
req.configuredRepos = make(map[string][]*common.AutogitConfig)
|
||||||
|
|
||||||
@@ -111,26 +119,67 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if CurrentUser, err = gitea.GetCurrentUser(); err != nil {
|
if CurrentUser, err = Gitea.GetCurrentUser(); err != nil {
|
||||||
common.LogError("Failed to fetch current gitea user:", err)
|
common.LogError("Failed to fetch current gitea user:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.LogInfo("Running with token from", CurrentUser.UserName)
|
common.LogInfo("Running with token from", CurrentUser.UserName)
|
||||||
|
|
||||||
req.Synced = &PullRequestSynced{
|
req.Synced = &PullRequestSynced{}
|
||||||
gitea: gitea,
|
req.Opened = &PullRequestOpened{}
|
||||||
}
|
req.Closed = &PullRequestClosed{}
|
||||||
req.Opened = &PullRequestOpened{
|
req.Review = &PullRequestReviewed{}
|
||||||
gitea: gitea,
|
|
||||||
}
|
if *pr != "" {
|
||||||
req.Closed = &PullRequestClosed{
|
rx := regexp.MustCompile("^([a-zA-Z0-9_\\.-]+)/([a-zA-Z0-9_\\.-]+)#([0-9]+)$")
|
||||||
gitea: gitea,
|
if data := rx.FindStringSubmatch(*pr); data == nil {
|
||||||
}
|
common.LogError("Cannot parse PR line to process", *pr)
|
||||||
req.Review = &PullRequestReviewed{
|
} else {
|
||||||
gitea: gitea,
|
org := data[1]
|
||||||
|
repo := data[2]
|
||||||
|
num, err := strconv.ParseInt(data[3], 10, 64)
|
||||||
|
common.LogInfo("Processing:", org, "/", repo, "#", num)
|
||||||
|
common.PanicOnError(err)
|
||||||
|
pr, err := Gitea.GetPullRequest(org, repo, num)
|
||||||
|
if err != nil {
|
||||||
|
common.LogError("Cannot fetch PR", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(*checkOnStart, req, gitea, time.Duration(*checkIntervalHours)*time.Hour)
|
if pr.State != "open" {
|
||||||
|
common.LogError("Can only deal with open PRs. This one is", pr.State)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c := configs.GetPrjGitConfig(org, repo, pr.Base.Ref)
|
||||||
|
if c == nil {
|
||||||
|
if pr.Base.Repo.DefaultBranch == pr.Base.Ref {
|
||||||
|
c = configs.GetPrjGitConfig(org, repo, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c == nil {
|
||||||
|
common.LogError("Cannot find config for PR.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
git, err := GitHandler.CreateGitHandler(org)
|
||||||
|
common.PanicOnError(err)
|
||||||
|
defer git.Close()
|
||||||
|
|
||||||
|
p := common.PullRequestWebhookEvent{
|
||||||
|
Action: "opened",
|
||||||
|
Number: num,
|
||||||
|
Pull_Request: common.PullRequestFromModel(pr),
|
||||||
|
}
|
||||||
|
if err = req.Opened.Process(&p, git, c); err != nil {
|
||||||
|
common.LogError("processor returned error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checker := CreateDefaultStateChecker(*checkOnStart, req, Gitea, time.Duration(*checkIntervalHours)*time.Hour)
|
||||||
go checker.ConsistencyCheckProcess()
|
go checker.ConsistencyCheckProcess()
|
||||||
|
|
||||||
listenDefs := common.ListenDefinitions{
|
listenDefs := common.ListenDefinitions{
|
||||||
|
|||||||
@@ -30,14 +30,26 @@ func (w *RequestProcessor) ProcessFunc(request *common.Request) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
org := req.Pull_Request.Base.Repo.Owner.Username
|
||||||
|
pkg := req.Pull_Request.Base.Repo.Name
|
||||||
|
branch := req.Pull_Request.Base.Ref
|
||||||
|
assumed_git_project_name := org + "/" + pkg + "#" + branch
|
||||||
|
|
||||||
var config *common.AutogitConfig
|
var config *common.AutogitConfig
|
||||||
for _, c := range configs {
|
for _, c := range configs {
|
||||||
if c.GitProjectName == req.Pull_Request.Base.Repo.Name ||
|
if c.GitProjectName == assumed_git_project_name {
|
||||||
c.Branch == req.Pull_Request.Base.Ref {
|
|
||||||
|
|
||||||
config = c
|
config = c
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Organization == org {
|
||||||
|
// default branch *or* match branch
|
||||||
|
if (c.Branch == "" && branch == req.Pull_Request.Base.Repo.Default_Branch) ||
|
||||||
|
(c.Branch != "" && c.Branch == branch) {
|
||||||
|
config = c
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return fmt.Errorf("Cannot find config for branch '%s'", req.Pull_Request.Base.Ref)
|
return fmt.Errorf("Cannot find config for branch '%s'", req.Pull_Request.Base.Ref)
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import (
|
|||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PullRequestClosed struct {
|
type PullRequestClosed struct{}
|
||||||
gitea common.Gitea
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*PullRequestClosed) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
func (*PullRequestClosed) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
||||||
if req.Repository.Name != config.GitProjectName {
|
if req.Repository.Name != config.GitProjectName {
|
||||||
|
|||||||
@@ -5,50 +5,61 @@ import (
|
|||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PullRequestOpened struct {
|
type PullRequestOpened struct{}
|
||||||
gitea common.Gitea
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *PullRequestOpened) CreateOrUpdatePrjGitPR(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
func (o *PullRequestOpened) CreateOrUpdatePrjGitPR(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
||||||
// create PrjGit branch for buidling the pull request
|
// create PrjGit branch for buidling the pull request
|
||||||
branchName := prGitBranchNameForPR(req)
|
branchName := prGitBranchNameForPR(req)
|
||||||
// TODO: fix this for config.Organization
|
// TODO: fix this for config.Organization
|
||||||
|
|
||||||
org, prj, _ := config.GetPrjGit()
|
org, prj, _ := config.GetPrjGit()
|
||||||
prjGit, err := o.gitea.CreateRepositoryIfNotExist(git, org, prj)
|
prjGit, err := Gitea.CreateRepositoryIfNotExist(git, org, prj)
|
||||||
common.PanicOnErrorWithMsg(err, "Error creating a prjgitrepo: "+err.Error())
|
common.PanicOnErrorWithMsg(err, "Error creating a prjgitrepo:", err)
|
||||||
|
|
||||||
remoteName, err := git.GitClone(common.DefaultGitPrj, config.Branch, prjGit.SSHURL)
|
remoteName, err := git.GitClone(common.DefaultGitPrj, config.Branch, prjGit.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
|
|
||||||
|
// check if branch already there, and check that out if available
|
||||||
|
if err := git.GitExec(common.DefaultGitPrj, "fetch", remoteName, branchName); err == nil {
|
||||||
|
git.GitExecOrPanic(common.DefaultGitPrj, "checkout", "-B", branchName, remoteName+"/"+branchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
prOrg := req.Pull_Request.Base.Repo.Owner.Username
|
||||||
|
prRepo := req.Pull_Request.Base.Repo.Name
|
||||||
|
|
||||||
commitMsg := fmt.Sprintf(`auto-created for %s
|
commitMsg := fmt.Sprintf(`auto-created for %s
|
||||||
|
|
||||||
This commit was autocreated by %s
|
This commit was autocreated by %s
|
||||||
referencing
|
referencing
|
||||||
|
|
||||||
`+common.PrPattern,
|
`+common.PrPattern,
|
||||||
req.Repository.Owner.Username,
|
prOrg,
|
||||||
req.Repository.Name,
|
prRepo,
|
||||||
GitAuthor,
|
GitAuthor,
|
||||||
req.Repository.Name,
|
prRepo,
|
||||||
req.Pull_Request.Number,
|
req.Pull_Request.Number,
|
||||||
)
|
)
|
||||||
|
|
||||||
subList, err := git.GitSubmoduleList(common.DefaultGitPrj, "HEAD")
|
subList, err := git.GitSubmoduleList(common.DefaultGitPrj, "HEAD")
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
|
|
||||||
if id := subList[req.Repository.Name]; id != req.Pull_Request.Head.Sha {
|
if id := subList[prRepo]; id != req.Pull_Request.Head.Sha {
|
||||||
updateSubmoduleInPR(req, git)
|
updateSubmoduleInPR(req, git)
|
||||||
|
status, err := git.GitStatus(common.DefaultGitPrj)
|
||||||
|
common.LogDebug("status:", status)
|
||||||
|
common.LogDebug("submodule", prRepo, " hash:", id, " -> ", req.Pull_Request.Head.Sha)
|
||||||
|
common.PanicOnError(err)
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", remoteName, "+HEAD:"+branchName))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", remoteName, "+HEAD:"+branchName))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = o.gitea.CreatePullRequestIfNotExist(prjGit, branchName, prjGit.DefaultBranch,
|
_, err = Gitea.CreatePullRequestIfNotExist(prjGit, branchName, prjGit.DefaultBranch,
|
||||||
fmt.Sprintf("Forwarded PR: %s", req.Repository.Name),
|
fmt.Sprintf("Forwarded PR: %s", prRepo),
|
||||||
fmt.Sprintf(`This is a forwarded pull request by %s
|
fmt.Sprintf(`This is a forwarded pull request by %s
|
||||||
referencing the following pull request:
|
referencing the following pull request:
|
||||||
|
|
||||||
`+common.PrPattern,
|
`+common.PrPattern,
|
||||||
GitAuthor, req.Repository.Owner.Username, req.Repository.Name, req.Pull_Request.Number),
|
GitAuthor, prOrg, prRepo, req.Pull_Request.Number),
|
||||||
)
|
)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@@ -57,27 +68,33 @@ referencing the following pull request:
|
|||||||
func (o *PullRequestOpened) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
func (o *PullRequestOpened) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
||||||
// requests against project are not handled here
|
// requests against project are not handled here
|
||||||
common.LogInfo("processing opened PR:", req.Pull_Request.Url)
|
common.LogInfo("processing opened PR:", req.Pull_Request.Url)
|
||||||
if org, repo, _ := config.GetPrjGit(); req.Repository.Owner.Username != org || req.Repository.Name != repo {
|
prOrg := req.Pull_Request.Base.Repo.Owner.Username
|
||||||
|
prRepo := req.Pull_Request.Base.Repo.Name
|
||||||
|
if prjGitOrg, prjGitRepo, _ := config.GetPrjGit(); prOrg != prjGitOrg || prRepo != prjGitRepo {
|
||||||
if err := o.CreateOrUpdatePrjGitPR(req, git, config); err != nil {
|
if err := o.CreateOrUpdatePrjGitPR(req, git, config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prset, err := common.FetchPRSet(o.gitea, req.Repository.Owner.Username, req.Repository.Name, req.Number, config)
|
prset, err := common.FetchPRSet(Gitea, prOrg, prRepo, req.Number, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
common.LogError("Cannot fetch PRSet:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
common.LogInfo("fetched PRSet of size:", len(prset.PRs))
|
||||||
|
|
||||||
// request build review
|
// request build review
|
||||||
PR, err := prset.GetPrjGitPR()
|
PR, err := prset.GetPrjGitPR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
common.LogError("Error fetching PrjGitPR:", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
common.LogDebug(" num of reviewers:", len(PR.RequestedReviewers))
|
common.LogDebug(" num of reviewers:", len(PR.RequestedReviewers))
|
||||||
maintainers, err := common.FetchProjectMaintainershipData(o.gitea, config.Organization, config.GitProjectName, config.Branch)
|
org, repo, branch := config.GetPrjGit()
|
||||||
|
maintainers, err := common.FetchProjectMaintainershipData(Gitea, org, repo, branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return prset.AssignReviewers(o.gitea, maintainers)
|
return prset.AssignReviewers(Gitea, maintainers)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,20 @@ package main
|
|||||||
|
|
||||||
import "src.opensuse.org/autogits/common"
|
import "src.opensuse.org/autogits/common"
|
||||||
|
|
||||||
type PullRequestReviewed struct {
|
type PullRequestReviewed struct{}
|
||||||
gitea common.Gitea
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *PullRequestReviewed) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
func (o *PullRequestReviewed) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
||||||
prset, err := common.FetchPRSet(o.gitea, req.Repository.Owner.Username, req.Repository.Name, req.Number, config)
|
prset, err := common.FetchPRSet(Gitea, req.Repository.Owner.Username, req.Repository.Name, req.Number, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
maintainers, err := common.FetchProjectMaintainershipData(o.gitea, prset.Config.Organization, prset.Config.GitProjectName, prset.Config.Branch)
|
maintainers, err := common.FetchProjectMaintainershipData(Gitea, prset.Config.Organization, prset.Config.GitProjectName, prset.Config.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if prset.IsApproved(o.gitea, maintainers) {
|
if prset.IsApproved(Gitea, maintainers) {
|
||||||
return prset.Merge(GitHandler)
|
return prset.Merge(GitHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func prGitBranchNameForPR(req *common.PullRequestWebhookEvent) string {
|
func prGitBranchNameForPR(req *common.PullRequestWebhookEvent) string {
|
||||||
return fmt.Sprintf("PR_%s#%d", req.Repository.Name, req.Pull_Request.Number)
|
return fmt.Sprintf("PR_%s#%d", req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSubmoduleInPR(req *common.PullRequestWebhookEvent, git common.Git) {
|
func updateSubmoduleInPR(req *common.PullRequestWebhookEvent, git common.Git) {
|
||||||
@@ -25,18 +25,17 @@ func processPrjGitPullRequestSync(req *common.PullRequestWebhookEvent) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type PullRequestSynced struct {
|
type PullRequestSynced struct{}
|
||||||
gitea common.Gitea
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *PullRequestSynced) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
func (o *PullRequestSynced) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
|
||||||
if req.Repository.Name == config.GitProjectName {
|
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
|
||||||
|
if req.Repository.Owner.Username == prjGitOrg && req.Repository.Name == prjGitRepo {
|
||||||
return processPrjGitPullRequestSync(req)
|
return processPrjGitPullRequestSync(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to verify that submodule in the PR for prjgit
|
// need to verify that submodule in the PR for prjgit
|
||||||
// is still pointing to the HEAD of the PR
|
// is still pointing to the HEAD of the PR
|
||||||
pr, err := o.gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, req.Number)
|
pr, err := Gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, req.Number)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot fetch PR data from gitea: %w", err)
|
return fmt.Errorf("Cannot fetch PR data from gitea: %w", err)
|
||||||
}
|
}
|
||||||
@@ -46,13 +45,15 @@ func (o *PullRequestSynced) Process(req *common.PullRequestWebhookEvent, git com
|
|||||||
return fmt.Errorf("Package update associated with invalid number of projects. Expected 1. Got %d", len(prs))
|
return fmt.Errorf("Package update associated with invalid number of projects. Expected 1. Got %d", len(prs))
|
||||||
}
|
}
|
||||||
|
|
||||||
prjPr, err := o.gitea.GetPullRequest(prs[0].Org, prs[0].Repo, prs[0].Num)
|
prjPr, err := Gitea.GetPullRequest(prs[0].Org, prs[0].Repo, prs[0].Num)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot get PrjGit PR in processPullRequestSync. Err: %w", err)
|
return fmt.Errorf("Cannot get PrjGit PR in processPullRequestSync. Err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
common.PanicOnError(git.GitExec("", "clone", "--branch", prjPr.Head.Name, "--depth", "1", prjPr.Head.Repo.SSHURL, common.DefaultGitPrj))
|
branchName := prGitBranchNameForPR(req)
|
||||||
|
remote, err := git.GitClone(common.DefaultGitPrj, branchName, prjPr.Head.Repo.SSHURL)
|
||||||
|
common.PanicOnError(err)
|
||||||
commitId, ok := git.GitSubmoduleCommitId(common.DefaultGitPrj, req.Repository.Name, prjPr.Head.Sha)
|
commitId, ok := git.GitSubmoduleCommitId(common.DefaultGitPrj, req.Repository.Name, prjPr.Head.Sha)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -74,9 +75,8 @@ Update to %s`, req.Pull_Request.Head.Sha)
|
|||||||
common.LogDebug("Creating new commit msg:", commitMsg)
|
common.LogDebug("Creating new commit msg:", commitMsg)
|
||||||
|
|
||||||
// we need to update prjgit PR with the new head hash
|
// we need to update prjgit PR with the new head hash
|
||||||
branchName := prGitBranchNameForPR(req)
|
|
||||||
updateSubmoduleInPR(req, git)
|
updateSubmoduleInPR(req, git)
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:"+branchName))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", remote))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,14 +26,12 @@ type DefaultStateChecker struct {
|
|||||||
checkOnStart bool
|
checkOnStart bool
|
||||||
checkInterval time.Duration
|
checkInterval time.Duration
|
||||||
|
|
||||||
gitea common.Gitea
|
|
||||||
processor *RequestProcessor
|
processor *RequestProcessor
|
||||||
i StateChecker
|
i StateChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDefaultStateChecker(checkOnStart bool, processor *RequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
|
func CreateDefaultStateChecker(checkOnStart bool, processor *RequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
|
||||||
var s = &DefaultStateChecker{
|
var s = &DefaultStateChecker{
|
||||||
gitea: gitea,
|
|
||||||
checkInterval: interval,
|
checkInterval: interval,
|
||||||
checkOnStart: checkOnStart,
|
checkOnStart: checkOnStart,
|
||||||
processor: processor,
|
processor: processor,
|
||||||
@@ -62,8 +60,10 @@ func (s *DefaultStateChecker) ProcessPR(git common.Git, pr *models.PullRequest,
|
|||||||
return fmt.Errorf("Unhandled pull request state: '%s'. %s/%s/%d", pr.State, config.Organization, "", pr.Index)
|
return fmt.Errorf("Unhandled pull request state: '%s'. %s/%s/%d", pr.State, config.Organization, "", pr.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
common.LogError(" * processor error returned:", err)
|
common.LogError(" * processor error returned:", err)
|
||||||
return nil
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) error {
|
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) error {
|
||||||
@@ -80,20 +80,20 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) e
|
|||||||
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
||||||
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
||||||
|
|
||||||
git, err := GitHandler.CreateGitHandler(prjGitOrg)
|
git, err := GitHandler.CreateGitHandler(config.Organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot create git handler: %w", err)
|
return fmt.Errorf("Cannot create git handler: %w", err)
|
||||||
}
|
}
|
||||||
defer git.Close()
|
defer git.Close()
|
||||||
|
|
||||||
repo, err := s.gitea.CreateRepositoryIfNotExist(git, prjGitOrg, prjGitRepo)
|
repo, err := Gitea.CreateRepositoryIfNotExist(git, prjGitOrg, prjGitRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error fetching or creating '%s/%s#%s' -- aborting verifyProjectState(). Err: %w", prjGitBranch, prjGitRepo, prjGitBranch, err)
|
return fmt.Errorf("Error fetching or creating '%s/%s#%s' -- aborting verifyProjectState(). Err: %w", prjGitBranch, prjGitRepo, prjGitBranch, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = git.GitClone(config.GitProjectName, prjGitBranch, repo.SSHURL)
|
_, err = git.GitClone(config.GitProjectName, prjGitBranch, repo.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
prs, err := s.gitea.GetRecentPullRequests(prjGitOrg, prjGitRepo, prjGitBranch)
|
prs, err := Gitea.GetRecentPullRequests(prjGitOrg, prjGitRepo, prjGitBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error fetching PrjGit Prs for %s/%s#%s: %w", prjGitOrg, prjGitRepo, prjGitBranch, err)
|
return fmt.Errorf("Error fetching PrjGit Prs for %s/%s#%s: %w", prjGitOrg, prjGitRepo, prjGitBranch, err)
|
||||||
}
|
}
|
||||||
@@ -114,9 +114,18 @@ nextSubmodule:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if open PR have PR against project
|
// check if open PR have PR against project
|
||||||
prs, err := s.gitea.GetRecentPullRequests(config.Organization, submoduleName, config.Branch)
|
branch := config.Branch
|
||||||
|
if branch == "" {
|
||||||
|
repo, err := Gitea.GetRepository(config.Organization, submoduleName)
|
||||||
|
if err != nil || repo == nil {
|
||||||
|
common.LogError("Cannot fetch repository:", config.Organization, "/", submoduleName, "... submodule skipped.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
branch = repo.DefaultBranch
|
||||||
|
}
|
||||||
|
prs, err := Gitea.GetRecentPullRequests(config.Organization, submoduleName, branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error fetching pull requests for %s/%s#%s. Err: %w", config.Organization, submoduleName, config.Branch, err)
|
return fmt.Errorf("Error fetching pull requests for %s/%s#%s. Err: %w", config.Organization, submoduleName, branch, err)
|
||||||
}
|
}
|
||||||
common.LogDebug(" - # of PRs to check:", len(prs))
|
common.LogDebug(" - # of PRs to check:", len(prs))
|
||||||
|
|
||||||
@@ -125,7 +134,7 @@ nextSubmodule:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if the commited changes are syned with branches
|
// check if the commited changes are syned with branches
|
||||||
commits, err := s.gitea.GetRecentCommits(config.Organization, submoduleName, config.Branch, 10)
|
commits, err := Gitea.GetRecentCommits(config.Organization, submoduleName, branch, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error fetching recent commits for %s/%s. Err: %w", config.Organization, submoduleName, err)
|
return fmt.Errorf("Error fetching recent commits for %s/%s. Err: %w", config.Organization, submoduleName, err)
|
||||||
}
|
}
|
||||||
@@ -143,18 +152,18 @@ nextSubmodule:
|
|||||||
// not found in past, check if we should advance the branch label ... pull the submodule
|
// not found in past, check if we should advance the branch label ... pull the submodule
|
||||||
git.GitExecOrPanic(config.GitProjectName, "submodule", "update", "--init", "--filter", "blob:none", "--", sub)
|
git.GitExecOrPanic(config.GitProjectName, "submodule", "update", "--init", "--filter", "blob:none", "--", sub)
|
||||||
subDir := path.Join(config.GitProjectName, sub)
|
subDir := path.Join(config.GitProjectName, sub)
|
||||||
newCommits := common.SplitStringNoEmpty(git.GitExecWithOutputOrPanic(subDir, "rev-list", "^origin/"+config.Branch, commitID), "\n")
|
newCommits := common.SplitStringNoEmpty(git.GitExecWithOutputOrPanic(subDir, "rev-list", "^origin/"+branch, commitID), "\n")
|
||||||
|
|
||||||
if len(newCommits) >= 1 {
|
if len(newCommits) >= 1 {
|
||||||
common.LogDebug(" - updating branch", config.Branch, "to new head", commitID, " - len:", len(newCommits))
|
common.LogDebug(" - updating branch", branch, "to new head", commitID, " - len:", len(newCommits))
|
||||||
git.GitExecOrPanic(subDir, "checkout", "-B", config.Branch, commitID)
|
git.GitExecOrPanic(subDir, "checkout", "-B", branch, commitID)
|
||||||
url := git.GitExecWithOutputOrPanic(subDir, "remote", "get-url", "origin", "--push")
|
url := git.GitExecWithOutputOrPanic(subDir, "remote", "get-url", "origin", "--push")
|
||||||
sshUrl, err := common.TranslateHttpsToSshUrl(strings.TrimSpace(url))
|
sshUrl, err := common.TranslateHttpsToSshUrl(strings.TrimSpace(url))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot traslate HTTPS git URL to SSH_URL. %w", err)
|
return fmt.Errorf("Cannot traslate HTTPS git URL to SSH_URL. %w", err)
|
||||||
}
|
}
|
||||||
git.GitExecOrPanic(subDir, "remote", "set-url", "origin", "--push", sshUrl)
|
git.GitExecOrPanic(subDir, "remote", "set-url", "origin", "--push", sshUrl)
|
||||||
git.GitExecOrPanic(subDir, "push", "origin", config.Branch)
|
git.GitExecOrPanic(subDir, "push", "origin", branch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user