workflow-pr: rename package branches when a package is removed
Integration (Selective) / check (pull_request) Successful in 2s
gofmt / Format checking (pull_request) Successful in 4s
go-generate-check / go-generate-check (pull_request) Successful in 12s
gofmt / Linter and static analysis (pull_request) Successful in 59s
go-test-unit / go-test-unit (pull_request) Successful in 1m1s
Integration (Selective) / integration (pull_request) Successful in 9m14s
Integration (Selective) / check (pull_request) Successful in 2s
gofmt / Format checking (pull_request) Successful in 4s
go-generate-check / go-generate-check (pull_request) Successful in 12s
gofmt / Linter and static analysis (pull_request) Successful in 59s
go-test-unit / go-test-unit (pull_request) Successful in 1m1s
Integration (Selective) / integration (pull_request) Successful in 9m14s
Implement the core of the package-removal workflow: when a package's submodule disappears from the project git, rename that package's project branch to its "-removed" variant (e.g. "factory" -> "factory-removed"). Renaming is anchored on the submodule disappearing, not on the removal issue, so it also happens when the issue process is bypassed. - common: add a RenameBranch Gitea primitive (wrapper over RepoRenameBranch) exposed through a small GiteaBranchRenamer interface and the master Gitea interface, with regenerated mocks. Renaming the default branch is handled by Gitea itself (it reassigns the default), so no SetDefaultBranch is needed. - common: add DetectRemovedSubmodules (diff two project-git submodule lists) and RenameRemovedPackageBranches (best-effort, idempotent: skip packages whose branch is already gone, resolve the default branch when config.Branch is empty, and keep going if one package fails). - workflow-pr: hook the rename into the two points where a submodule can disappear: after a ProjectGit PR merge (reusing the already computed removed-submodule set) and in the consistency check (comparing the previous project HEAD with the freshly cloned one), the latter covering bypassed removals such as a direct push. Assisted-by: Anthropic:claude-opus-4.8
This commit is contained in:
@@ -205,6 +205,11 @@ type GiteaRepoReparentAccepter interface {
|
||||
AcceptRepoReparent(owner, repo string) (*models.Repository, error)
|
||||
}
|
||||
|
||||
type GiteaBranchRenamer interface {
|
||||
Context() context.Context
|
||||
RenameBranch(org, repo, oldName, newName string) error
|
||||
}
|
||||
|
||||
type GiteaFileContentReader interface {
|
||||
Context() context.Context
|
||||
GetRepositoryFileContent(org, repo, hash, path string) ([]byte, string, error)
|
||||
@@ -257,6 +262,7 @@ type Gitea interface {
|
||||
GiteaLabelRemover
|
||||
GiteaIssueFetcher
|
||||
GiteaRepoReparentAccepter
|
||||
GiteaBranchRenamer
|
||||
|
||||
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
|
||||
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
|
||||
@@ -1230,6 +1236,22 @@ func (gitea *GiteaTransport) DeleteBranch(org, repo, branch string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (gitea *GiteaTransport) RenameBranch(org, repo, oldName, newName string) error {
|
||||
_, err := gitea.client.Repository.RepoRenameBranch(
|
||||
repository.NewRepoRenameBranchParams().
|
||||
WithOwner(org).
|
||||
WithRepo(repo).
|
||||
WithBranch(oldName).
|
||||
WithBody(&models.RenameBranchRepoOption{Name: &newName}),
|
||||
gitea.transport.DefaultAuthentication,
|
||||
)
|
||||
if err != nil {
|
||||
LogErrorCtx(gitea.Context(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gitea *GiteaTransport) GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error) {
|
||||
not := false
|
||||
var page int64 = 1
|
||||
|
||||
@@ -3732,6 +3732,106 @@ func (c *MockGiteaRepoReparentAccepterContextCall) DoAndReturn(f func() context.
|
||||
return c
|
||||
}
|
||||
|
||||
// MockGiteaBranchRenamer is a mock of GiteaBranchRenamer interface.
|
||||
type MockGiteaBranchRenamer struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockGiteaBranchRenamerMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockGiteaBranchRenamerMockRecorder is the mock recorder for MockGiteaBranchRenamer.
|
||||
type MockGiteaBranchRenamerMockRecorder struct {
|
||||
mock *MockGiteaBranchRenamer
|
||||
}
|
||||
|
||||
// NewMockGiteaBranchRenamer creates a new mock instance.
|
||||
func NewMockGiteaBranchRenamer(ctrl *gomock.Controller) *MockGiteaBranchRenamer {
|
||||
mock := &MockGiteaBranchRenamer{ctrl: ctrl}
|
||||
mock.recorder = &MockGiteaBranchRenamerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockGiteaBranchRenamer) EXPECT() *MockGiteaBranchRenamerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Context mocks base method.
|
||||
func (m *MockGiteaBranchRenamer) Context() context.Context {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Context")
|
||||
ret0, _ := ret[0].(context.Context)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Context indicates an expected call of Context.
|
||||
func (mr *MockGiteaBranchRenamerMockRecorder) Context() *MockGiteaBranchRenamerContextCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockGiteaBranchRenamer)(nil).Context))
|
||||
return &MockGiteaBranchRenamerContextCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaBranchRenamerContextCall wrap *gomock.Call
|
||||
type MockGiteaBranchRenamerContextCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaBranchRenamerContextCall) Return(arg0 context.Context) *MockGiteaBranchRenamerContextCall {
|
||||
c.Call = c.Call.Return(arg0)
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaBranchRenamerContextCall) Do(f func() context.Context) *MockGiteaBranchRenamerContextCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaBranchRenamerContextCall) DoAndReturn(f func() context.Context) *MockGiteaBranchRenamerContextCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// RenameBranch mocks base method.
|
||||
func (m *MockGiteaBranchRenamer) RenameBranch(org, repo, oldName, newName string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RenameBranch", org, repo, oldName, newName)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RenameBranch indicates an expected call of RenameBranch.
|
||||
func (mr *MockGiteaBranchRenamerMockRecorder) RenameBranch(org, repo, oldName, newName any) *MockGiteaBranchRenamerRenameBranchCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameBranch", reflect.TypeOf((*MockGiteaBranchRenamer)(nil).RenameBranch), org, repo, oldName, newName)
|
||||
return &MockGiteaBranchRenamerRenameBranchCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaBranchRenamerRenameBranchCall wrap *gomock.Call
|
||||
type MockGiteaBranchRenamerRenameBranchCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaBranchRenamerRenameBranchCall) Return(arg0 error) *MockGiteaBranchRenamerRenameBranchCall {
|
||||
c.Call = c.Call.Return(arg0)
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaBranchRenamerRenameBranchCall) Do(f func(string, string, string, string) error) *MockGiteaBranchRenamerRenameBranchCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaBranchRenamerRenameBranchCall) DoAndReturn(f func(string, string, string, string) error) *MockGiteaBranchRenamerRenameBranchCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// MockGiteaFileContentReader is a mock of GiteaFileContentReader interface.
|
||||
type MockGiteaFileContentReader struct {
|
||||
ctrl *gomock.Controller
|
||||
@@ -5523,6 +5623,44 @@ func (c *MockGiteaRemoveLabelCall) DoAndReturn(f func(string, string, int64, str
|
||||
return c
|
||||
}
|
||||
|
||||
// RenameBranch mocks base method.
|
||||
func (m *MockGitea) RenameBranch(org, repo, oldName, newName string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RenameBranch", org, repo, oldName, newName)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RenameBranch indicates an expected call of RenameBranch.
|
||||
func (mr *MockGiteaMockRecorder) RenameBranch(org, repo, oldName, newName any) *MockGiteaRenameBranchCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameBranch", reflect.TypeOf((*MockGitea)(nil).RenameBranch), org, repo, oldName, newName)
|
||||
return &MockGiteaRenameBranchCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaRenameBranchCall wrap *gomock.Call
|
||||
type MockGiteaRenameBranchCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaRenameBranchCall) Return(arg0 error) *MockGiteaRenameBranchCall {
|
||||
c.Call = c.Call.Return(arg0)
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaRenameBranchCall) Do(f func(string, string, string, string) error) *MockGiteaRenameBranchCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaRenameBranchCall) DoAndReturn(f func(string, string, string, string) error) *MockGiteaRenameBranchCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// RequestReviews mocks base method.
|
||||
func (m *MockGitea) RequestReviews(pr *models.PullRequest, reviewer ...string) ([]*models.PullReview, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package common
|
||||
|
||||
/*
|
||||
* This file is part of Autogits.
|
||||
*
|
||||
* Copyright © 2024 SUSE LLC
|
||||
*
|
||||
* Autogits is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Autogits is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* Foobar. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
|
||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||
)
|
||||
|
||||
// GiteaPackageBranchRemover is the Gitea surface needed to rename a package's
|
||||
// project branch to its removed-suffix variant: resolve the branch, check it
|
||||
// still exists and rename it.
|
||||
type GiteaPackageBranchRemover interface {
|
||||
Context() context.Context
|
||||
GiteaBranchRenamer
|
||||
GetRepository(org, repo string) (*models.Repository, error)
|
||||
GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error)
|
||||
}
|
||||
|
||||
// DetectRemovedSubmodules returns the submodule paths that exist in fromCommit
|
||||
// but no longer exist in toCommit of the project git checked out at prjPath.
|
||||
func DetectRemovedSubmodules(git GitSubmoduleLister, prjPath, fromCommit, toCommit string) ([]string, error) {
|
||||
before, err := git.GitSubmoduleList(prjPath, fromCommit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
after, err := git.GitSubmoduleList(prjPath, toCommit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
removed := make([]string, 0)
|
||||
for submodulePath := range before {
|
||||
if _, found := after[submodulePath]; !found {
|
||||
removed = append(removed, submodulePath)
|
||||
}
|
||||
}
|
||||
return removed, nil
|
||||
}
|
||||
|
||||
// RenameRemovedPackageBranches renames the project branch of each removed
|
||||
// package to its removed-suffix variant (e.g. "factory" -> "factory-removed")
|
||||
// in org. When branch is empty, each package's default branch is used.
|
||||
//
|
||||
// It is best-effort and idempotent: packages whose branch no longer exists
|
||||
// (already renamed) are skipped, and a failure to rename one package does not
|
||||
// prevent the others from being processed.
|
||||
func RenameRemovedPackageBranches(ctx context.Context, gitea GiteaPackageBranchRemover, org, branch string, removedPackages []string) {
|
||||
for _, removedPath := range removedPackages {
|
||||
pkg := path.Base(removedPath)
|
||||
LogInfoCtx(ctx, "Detected package removal:", org+"/"+pkg)
|
||||
|
||||
pkgBranch := branch
|
||||
if pkgBranch == "" {
|
||||
repo, err := gitea.GetRepository(org, pkg)
|
||||
if err != nil || repo == nil {
|
||||
LogErrorCtx(ctx, "Cannot fetch repository", org+"/"+pkg, "to resolve default branch:", err)
|
||||
continue
|
||||
}
|
||||
pkgBranch = repo.DefaultBranch
|
||||
}
|
||||
|
||||
commits, err := gitea.GetRecentCommits(org, pkg, pkgBranch, 1)
|
||||
if err != nil {
|
||||
LogErrorCtx(ctx, "Cannot check if branch", pkgBranch, "exists in", org+"/"+pkg, ":", err)
|
||||
continue
|
||||
}
|
||||
if len(commits) == 0 {
|
||||
LogInfoCtx(ctx, "Branch", pkgBranch, "does not exist in", org+"/"+pkg, "(already removed?). Skipping rename.")
|
||||
continue
|
||||
}
|
||||
|
||||
targetBranch := pkgBranch + RemovedBranchSuffix
|
||||
if err := gitea.RenameBranch(org, pkg, pkgBranch, targetBranch); err != nil {
|
||||
LogErrorCtx(ctx, "Failed to rename branch", pkgBranch, "to", targetBranch, "in", org+"/"+pkg, ":", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package common_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
"src.opensuse.org/autogits/common"
|
||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||
mock_common "src.opensuse.org/autogits/common/mock"
|
||||
)
|
||||
|
||||
func TestDetectRemovedSubmodules(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
before map[string]string
|
||||
after map[string]string
|
||||
removed []string
|
||||
}{
|
||||
{
|
||||
name: "nothing removed",
|
||||
before: map[string]string{"pkgA": "a1", "pkgB": "b1"},
|
||||
after: map[string]string{"pkgA": "a1", "pkgB": "b1"},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
name: "one removed",
|
||||
before: map[string]string{"pkgA": "a1", "pkgB": "b1"},
|
||||
after: map[string]string{"pkgA": "a1"},
|
||||
removed: []string{"pkgB"},
|
||||
},
|
||||
{
|
||||
name: "multiple removed",
|
||||
before: map[string]string{"pkgA": "a1", "pkgB": "b1", "pkgC": "c1"},
|
||||
after: map[string]string{"pkgB": "b1"},
|
||||
removed: []string{"pkgA", "pkgC"},
|
||||
},
|
||||
{
|
||||
name: "modified is not removed",
|
||||
before: map[string]string{"pkgA": "a1"},
|
||||
after: map[string]string{"pkgA": "a2"},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
name: "added is not removed",
|
||||
before: map[string]string{"pkgA": "a1"},
|
||||
after: map[string]string{"pkgA": "a1", "pkgB": "b1"},
|
||||
removed: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
git := mock_common.NewMockGitSubmoduleLister(ctl)
|
||||
git.EXPECT().GitSubmoduleList("prj", "from").Return(test.before, nil)
|
||||
git.EXPECT().GitSubmoduleList("prj", "to").Return(test.after, nil)
|
||||
|
||||
removed, err := common.DetectRemovedSubmodules(git, "prj", "from", "to")
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
slices.Sort(removed)
|
||||
if !slices.Equal(removed, test.removed) {
|
||||
t.Fatalf("expected removed %v, got %v", test.removed, removed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectRemovedSubmodulesError(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
git := mock_common.NewMockGitSubmoduleLister(ctl)
|
||||
git.EXPECT().GitSubmoduleList("prj", "from").Return(nil, fmt.Errorf("boom"))
|
||||
|
||||
if _, err := common.DetectRemovedSubmodules(git, "prj", "from", "to"); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameRemovedPackageBranches(t *testing.T) {
|
||||
commit := []*models.Commit{{SHA: "abc"}}
|
||||
|
||||
t.Run("renames existing branch", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().GetRecentCommits("pool", "pkgA", "factory", int64(1)).Return(commit, nil)
|
||||
gitea.EXPECT().RenameBranch("pool", "pkgA", "factory", "factory-removed").Return(nil)
|
||||
|
||||
common.RenameRemovedPackageBranches(context.Background(), gitea, "pool", "factory", []string{"pkgA"})
|
||||
})
|
||||
|
||||
t.Run("skips already-removed branch", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
// Branch no longer exists -> no RenameBranch call expected.
|
||||
gitea.EXPECT().GetRecentCommits("pool", "pkgA", "factory", int64(1)).Return(nil, nil)
|
||||
|
||||
common.RenameRemovedPackageBranches(context.Background(), gitea, "pool", "factory", []string{"pkgA"})
|
||||
})
|
||||
|
||||
t.Run("uses base name of submodule path", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().GetRecentCommits("pool", "pkgA", "factory", int64(1)).Return(commit, nil)
|
||||
gitea.EXPECT().RenameBranch("pool", "pkgA", "factory", "factory-removed").Return(nil)
|
||||
|
||||
common.RenameRemovedPackageBranches(context.Background(), gitea, "pool", "factory", []string{"some/dir/pkgA"})
|
||||
})
|
||||
|
||||
t.Run("resolves default branch when branch is empty", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().GetRepository("pool", "pkgA").Return(&models.Repository{DefaultBranch: "factory"}, nil)
|
||||
gitea.EXPECT().GetRecentCommits("pool", "pkgA", "factory", int64(1)).Return(commit, nil)
|
||||
gitea.EXPECT().RenameBranch("pool", "pkgA", "factory", "factory-removed").Return(nil)
|
||||
|
||||
common.RenameRemovedPackageBranches(context.Background(), gitea, "pool", "", []string{"pkgA"})
|
||||
})
|
||||
|
||||
t.Run("continues after a rename failure", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().GetRecentCommits("pool", "pkgA", "factory", int64(1)).Return(commit, nil)
|
||||
gitea.EXPECT().RenameBranch("pool", "pkgA", "factory", "factory-removed").Return(fmt.Errorf("403"))
|
||||
gitea.EXPECT().GetRecentCommits("pool", "pkgB", "factory", int64(1)).Return(commit, nil)
|
||||
gitea.EXPECT().RenameBranch("pool", "pkgB", "factory", "factory-removed").Return(nil)
|
||||
|
||||
common.RenameRemovedPackageBranches(context.Background(), gitea, "pool", "factory", []string{"pkgA", "pkgB"})
|
||||
})
|
||||
}
|
||||
+5
-1
@@ -390,9 +390,13 @@ func (d DevelProjects) GetDevelProject(pkg string) (string, error) {
|
||||
return "", DevelProjectNotFound
|
||||
}
|
||||
|
||||
// RemovedBranchSuffix is the suffix appended to a package's project branch when
|
||||
// the package is removed (e.g. "factory" -> "factory-removed").
|
||||
const RemovedBranchSuffix = "-removed"
|
||||
|
||||
var removedBranchNameSuffixes []string = []string{
|
||||
"-rm",
|
||||
"-removed",
|
||||
RemovedBranchSuffix,
|
||||
"-deleted",
|
||||
}
|
||||
|
||||
|
||||
@@ -932,6 +932,16 @@ func (prp *PRProcessor) Process(ctx context.Context, req *models.PullRequest) (e
|
||||
}
|
||||
|
||||
PrjGitSubmoduleCheck(prp.ctx, config, git, prjPath, pkgs)
|
||||
|
||||
removed := make([]string, 0)
|
||||
for pkg, commit := range pkgs {
|
||||
if commit == "" {
|
||||
removed = append(removed, pkg)
|
||||
}
|
||||
}
|
||||
if len(removed) > 0 {
|
||||
common.RenameRemovedPackageBranches(prp.ctx, Gitea.WithContext(prp.ctx), config.Organization, config.Branch, removed)
|
||||
}
|
||||
}
|
||||
|
||||
// manually merge or close entire prset that is still open
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
@@ -136,9 +137,29 @@ func (s *DefaultStateChecker) VerifyProjectState(ctx context.Context, config *co
|
||||
return nil, fmt.Errorf("Error fetching or creating '%s' -- aborting verifyProjectState(). Err: %w", common.TaskRefBranch(prjGitOrg, prjGitRepo, prjGitBranch), err)
|
||||
}
|
||||
|
||||
var oldHead string
|
||||
if fi, errStat := os.Stat(path.Join(git.GetPath(), prjPath, ".git")); errStat == nil && fi.IsDir() {
|
||||
if out, errHead := git.GitExecWithOutput(prjPath, "rev-parse", "HEAD"); errHead == nil {
|
||||
oldHead = strings.TrimSpace(out)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = git.GitClone(prjPath, prjGitBranch, PrjGitRepo.SSHURL)
|
||||
common.PanicOnError(err)
|
||||
|
||||
// Detect submodule removals that happened outside the bot's PR flow (e.g. a
|
||||
// direct push to the project branch) and rename the affected package branches.
|
||||
if oldHead != "" {
|
||||
if newHead, errHead := git.GitBranchHead(prjPath, prjGitBranch); errHead == nil && oldHead != newHead {
|
||||
if removed, errDetect := common.DetectRemovedSubmodules(git, prjPath, oldHead, newHead); errDetect != nil {
|
||||
common.LogErrorCtx(ctx, "Cannot detect removed submodules in", common.TaskRefBranch(prjGitOrg, prjGitRepo, prjGitBranch), ":", errDetect)
|
||||
} else if len(removed) > 0 {
|
||||
common.LogInfoCtx(ctx, "Project repository updated from", oldHead, "to", newHead, "- renaming removed package branches")
|
||||
common.RenameRemovedPackageBranches(ctx, Gitea.WithContext(ctx), config.Organization, config.Branch, removed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
||||
Org: prjGitOrg,
|
||||
Repo: prjGitRepo,
|
||||
|
||||
Reference in New Issue
Block a user