mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Add priority to protected branch (#32286)
## Solves Currently for rules to re-order them you have to alter the creation date. so you basicly have to delete and recreate them in the right order. This is more than just inconvinient ... ## Solution Add a new col for prioritization ## Demo WebUI Video https://github.com/user-attachments/assets/92182a31-9705-4ac5-b6e3-9bb74108cbd1 --- *Sponsored by Kithara Software GmbH*
This commit is contained in:
		| @@ -34,6 +34,7 @@ type ProtectedBranch struct { | |||||||
| 	RepoID                        int64                  `xorm:"UNIQUE(s)"` | 	RepoID                        int64                  `xorm:"UNIQUE(s)"` | ||||||
| 	Repo                          *repo_model.Repository `xorm:"-"` | 	Repo                          *repo_model.Repository `xorm:"-"` | ||||||
| 	RuleName                      string                 `xorm:"'branch_name' UNIQUE(s)"` // a branch name or a glob match to branch name | 	RuleName                      string                 `xorm:"'branch_name' UNIQUE(s)"` // a branch name or a glob match to branch name | ||||||
|  | 	Priority                      int64                  `xorm:"NOT NULL DEFAULT 0"` | ||||||
| 	globRule                      glob.Glob              `xorm:"-"` | 	globRule                      glob.Glob              `xorm:"-"` | ||||||
| 	isPlainName                   bool                   `xorm:"-"` | 	isPlainName                   bool                   `xorm:"-"` | ||||||
| 	CanPush                       bool                   `xorm:"NOT NULL DEFAULT false"` | 	CanPush                       bool                   `xorm:"NOT NULL DEFAULT false"` | ||||||
| @@ -413,14 +414,27 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote | |||||||
| 	} | 	} | ||||||
| 	protectBranch.ApprovalsWhitelistTeamIDs = whitelist | 	protectBranch.ApprovalsWhitelistTeamIDs = whitelist | ||||||
|  |  | ||||||
| 	// Make sure protectBranch.ID is not 0 for whitelists | 	// Looks like it's a new rule | ||||||
| 	if protectBranch.ID == 0 { | 	if protectBranch.ID == 0 { | ||||||
|  | 		// as it's a new rule and if priority was not set, we need to calc it. | ||||||
|  | 		if protectBranch.Priority == 0 { | ||||||
|  | 			var lowestPrio int64 | ||||||
|  | 			// because of mssql we can not use builder or save xorm syntax, so raw sql it is | ||||||
|  | 			if _, err := db.GetEngine(ctx).SQL(`SELECT MAX(priority) FROM protected_branch WHERE repo_id = ?`, protectBranch.RepoID). | ||||||
|  | 				Get(&lowestPrio); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			log.Trace("Create new ProtectedBranch at repo[%d] and detect current lowest priority '%d'", protectBranch.RepoID, lowestPrio) | ||||||
|  | 			protectBranch.Priority = lowestPrio + 1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if _, err = db.GetEngine(ctx).Insert(protectBranch); err != nil { | 		if _, err = db.GetEngine(ctx).Insert(protectBranch); err != nil { | ||||||
| 			return fmt.Errorf("Insert: %v", err) | 			return fmt.Errorf("Insert: %v", err) | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// update the rule | ||||||
| 	if _, err = db.GetEngine(ctx).ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil { | 	if _, err = db.GetEngine(ctx).ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil { | ||||||
| 		return fmt.Errorf("Update: %v", err) | 		return fmt.Errorf("Update: %v", err) | ||||||
| 	} | 	} | ||||||
| @@ -428,6 +442,24 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func UpdateProtectBranchPriorities(ctx context.Context, repo *repo_model.Repository, ids []int64) error { | ||||||
|  | 	prio := int64(1) | ||||||
|  | 	return db.WithTx(ctx, func(ctx context.Context) error { | ||||||
|  | 		for _, id := range ids { | ||||||
|  | 			if _, err := db.GetEngine(ctx). | ||||||
|  | 				ID(id).Where("repo_id = ?", repo.ID). | ||||||
|  | 				Cols("priority"). | ||||||
|  | 				Update(&ProtectedBranch{ | ||||||
|  | 					Priority: prio, | ||||||
|  | 				}); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			prio++ | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
| // updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with | // updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with | ||||||
| // the users from newWhitelist which have explicit read or write access to the repo. | // the users from newWhitelist which have explicit read or write access to the repo. | ||||||
| func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) { | func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) { | ||||||
|   | |||||||
| @@ -28,6 +28,13 @@ func (rules ProtectedBranchRules) sort() { | |||||||
| 	sort.Slice(rules, func(i, j int) bool { | 	sort.Slice(rules, func(i, j int) bool { | ||||||
| 		rules[i].loadGlob() | 		rules[i].loadGlob() | ||||||
| 		rules[j].loadGlob() | 		rules[j].loadGlob() | ||||||
|  |  | ||||||
|  | 		// if priority differ, use that to sort | ||||||
|  | 		if rules[i].Priority != rules[j].Priority { | ||||||
|  | 			return rules[i].Priority < rules[j].Priority | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// now we sort the old way | ||||||
| 		if rules[i].isPlainName != rules[j].isPlainName { | 		if rules[i].isPlainName != rules[j].isPlainName { | ||||||
| 			return rules[i].isPlainName // plain name comes first, so plain name means "less" | 			return rules[i].isPlainName // plain name comes first, so plain name means "less" | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ func TestBranchRuleMatchPriority(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBranchRuleSort(t *testing.T) { | func TestBranchRuleSortLegacy(t *testing.T) { | ||||||
| 	in := []*ProtectedBranch{{ | 	in := []*ProtectedBranch{{ | ||||||
| 		RuleName:    "b", | 		RuleName:    "b", | ||||||
| 		CreatedUnix: 1, | 		CreatedUnix: 1, | ||||||
| @@ -103,3 +103,37 @@ func TestBranchRuleSort(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	assert.Equal(t, expect, got) | 	assert.Equal(t, expect, got) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestBranchRuleSortPriority(t *testing.T) { | ||||||
|  | 	in := []*ProtectedBranch{{ | ||||||
|  | 		RuleName:    "b", | ||||||
|  | 		CreatedUnix: 1, | ||||||
|  | 		Priority:    4, | ||||||
|  | 	}, { | ||||||
|  | 		RuleName:    "b/*", | ||||||
|  | 		CreatedUnix: 3, | ||||||
|  | 		Priority:    2, | ||||||
|  | 	}, { | ||||||
|  | 		RuleName:    "a/*", | ||||||
|  | 		CreatedUnix: 2, | ||||||
|  | 		Priority:    1, | ||||||
|  | 	}, { | ||||||
|  | 		RuleName:    "c", | ||||||
|  | 		CreatedUnix: 0, | ||||||
|  | 		Priority:    0, | ||||||
|  | 	}, { | ||||||
|  | 		RuleName:    "a", | ||||||
|  | 		CreatedUnix: 4, | ||||||
|  | 		Priority:    3, | ||||||
|  | 	}} | ||||||
|  | 	expect := []string{"c", "a/*", "b/*", "a", "b"} | ||||||
|  |  | ||||||
|  | 	pbr := ProtectedBranchRules(in) | ||||||
|  | 	pbr.sort() | ||||||
|  |  | ||||||
|  | 	var got []string | ||||||
|  | 	for i := range pbr { | ||||||
|  | 		got = append(got, pbr[i].RuleName) | ||||||
|  | 	} | ||||||
|  | 	assert.Equal(t, expect, got) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -7,6 +7,10 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -76,3 +80,77 @@ func TestBranchRuleMatch(t *testing.T) { | |||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestUpdateProtectBranchPriorities(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
|  |  | ||||||
|  | 	// Create some test protected branches with initial priorities | ||||||
|  | 	protectedBranches := []*ProtectedBranch{ | ||||||
|  | 		{ | ||||||
|  | 			RepoID:   repo.ID, | ||||||
|  | 			RuleName: "master", | ||||||
|  | 			Priority: 1, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			RepoID:   repo.ID, | ||||||
|  | 			RuleName: "develop", | ||||||
|  | 			Priority: 2, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			RepoID:   repo.ID, | ||||||
|  | 			RuleName: "feature/*", | ||||||
|  | 			Priority: 3, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, pb := range protectedBranches { | ||||||
|  | 		_, err := db.GetEngine(db.DefaultContext).Insert(pb) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Test updating priorities | ||||||
|  | 	newPriorities := []int64{protectedBranches[2].ID, protectedBranches[0].ID, protectedBranches[1].ID} | ||||||
|  | 	err := UpdateProtectBranchPriorities(db.DefaultContext, repo, newPriorities) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	// Verify new priorities | ||||||
|  | 	pbs, err := FindRepoProtectedBranchRules(db.DefaultContext, repo.ID) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	expectedPriorities := map[string]int64{ | ||||||
|  | 		"feature/*": 1, | ||||||
|  | 		"master":    2, | ||||||
|  | 		"develop":   3, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, pb := range pbs { | ||||||
|  | 		assert.Equal(t, expectedPriorities[pb.RuleName], pb.Priority) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewProtectBranchPriority(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
|  |  | ||||||
|  | 	err := UpdateProtectBranch(db.DefaultContext, repo, &ProtectedBranch{ | ||||||
|  | 		RepoID:   repo.ID, | ||||||
|  | 		RuleName: "branch-1", | ||||||
|  | 		Priority: 1, | ||||||
|  | 	}, WhitelistOptions{}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	newPB := &ProtectedBranch{ | ||||||
|  | 		RepoID:   repo.ID, | ||||||
|  | 		RuleName: "branch-2", | ||||||
|  | 		// Priority intentionally omitted | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = UpdateProtectBranch(db.DefaultContext, repo, newPB, WhitelistOptions{}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	savedPB2, err := GetFirstMatchProtectedBranchRule(db.DefaultContext, repo.ID, "branch-2") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, int64(2), savedPB2.Priority) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -367,6 +367,7 @@ func prepareMigrationTasks() []*migration { | |||||||
| 		newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate), | 		newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate), | ||||||
| 		newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard), | 		newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard), | ||||||
| 		newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices), | 		newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices), | ||||||
|  | 		newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch), | ||||||
| 	} | 	} | ||||||
| 	return preparedMigrations | 	return preparedMigrations | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								models/migrations/v1_23/v310.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								models/migrations/v1_23/v310.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2024 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package v1_23 //nolint | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"xorm.io/xorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func AddPriorityToProtectedBranch(x *xorm.Engine) error { | ||||||
|  | 	type ProtectedBranch struct { | ||||||
|  | 		Priority int64 `xorm:"NOT NULL DEFAULT 0"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return x.Sync(new(ProtectedBranch)) | ||||||
|  | } | ||||||
| @@ -25,6 +25,7 @@ type BranchProtection struct { | |||||||
| 	// Deprecated: true | 	// Deprecated: true | ||||||
| 	BranchName                    string   `json:"branch_name"` | 	BranchName                    string   `json:"branch_name"` | ||||||
| 	RuleName                      string   `json:"rule_name"` | 	RuleName                      string   `json:"rule_name"` | ||||||
|  | 	Priority                      int64    `json:"priority"` | ||||||
| 	EnablePush                    bool     `json:"enable_push"` | 	EnablePush                    bool     `json:"enable_push"` | ||||||
| 	EnablePushWhitelist           bool     `json:"enable_push_whitelist"` | 	EnablePushWhitelist           bool     `json:"enable_push_whitelist"` | ||||||
| 	PushWhitelistUsernames        []string `json:"push_whitelist_usernames"` | 	PushWhitelistUsernames        []string `json:"push_whitelist_usernames"` | ||||||
| @@ -64,6 +65,7 @@ type CreateBranchProtectionOption struct { | |||||||
| 	// Deprecated: true | 	// Deprecated: true | ||||||
| 	BranchName                    string   `json:"branch_name"` | 	BranchName                    string   `json:"branch_name"` | ||||||
| 	RuleName                      string   `json:"rule_name"` | 	RuleName                      string   `json:"rule_name"` | ||||||
|  | 	Priority                      int64    `json:"priority"` | ||||||
| 	EnablePush                    bool     `json:"enable_push"` | 	EnablePush                    bool     `json:"enable_push"` | ||||||
| 	EnablePushWhitelist           bool     `json:"enable_push_whitelist"` | 	EnablePushWhitelist           bool     `json:"enable_push_whitelist"` | ||||||
| 	PushWhitelistUsernames        []string `json:"push_whitelist_usernames"` | 	PushWhitelistUsernames        []string `json:"push_whitelist_usernames"` | ||||||
| @@ -96,6 +98,7 @@ type CreateBranchProtectionOption struct { | |||||||
|  |  | ||||||
| // EditBranchProtectionOption options for editing a branch protection | // EditBranchProtectionOption options for editing a branch protection | ||||||
| type EditBranchProtectionOption struct { | type EditBranchProtectionOption struct { | ||||||
|  | 	Priority                      *int64   `json:"priority"` | ||||||
| 	EnablePush                    *bool    `json:"enable_push"` | 	EnablePush                    *bool    `json:"enable_push"` | ||||||
| 	EnablePushWhitelist           *bool    `json:"enable_push_whitelist"` | 	EnablePushWhitelist           *bool    `json:"enable_push_whitelist"` | ||||||
| 	PushWhitelistUsernames        []string `json:"push_whitelist_usernames"` | 	PushWhitelistUsernames        []string `json:"push_whitelist_usernames"` | ||||||
| @@ -125,3 +128,8 @@ type EditBranchProtectionOption struct { | |||||||
| 	UnprotectedFilePatterns       *string  `json:"unprotected_file_patterns"` | 	UnprotectedFilePatterns       *string  `json:"unprotected_file_patterns"` | ||||||
| 	BlockAdminMergeOverride       *bool    `json:"block_admin_merge_override"` | 	BlockAdminMergeOverride       *bool    `json:"block_admin_merge_override"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpdateBranchProtectionPriories a list to update the branch protection rule priorities | ||||||
|  | type UpdateBranchProtectionPriories struct { | ||||||
|  | 	IDs []int64 `json:"ids"` | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1204,6 +1204,7 @@ func Routes() *web.Router { | |||||||
| 						m.Patch("", bind(api.EditBranchProtectionOption{}), mustNotBeArchived, repo.EditBranchProtection) | 						m.Patch("", bind(api.EditBranchProtectionOption{}), mustNotBeArchived, repo.EditBranchProtection) | ||||||
| 						m.Delete("", repo.DeleteBranchProtection) | 						m.Delete("", repo.DeleteBranchProtection) | ||||||
| 					}) | 					}) | ||||||
|  | 					m.Post("/priority", bind(api.UpdateBranchProtectionPriories{}), mustNotBeArchived, repo.UpdateBranchProtectionPriories) | ||||||
| 				}, reqToken(), reqAdmin()) | 				}, reqToken(), reqAdmin()) | ||||||
| 				m.Group("/tags", func() { | 				m.Group("/tags", func() { | ||||||
| 					m.Get("", repo.ListTags) | 					m.Get("", repo.ListTags) | ||||||
|   | |||||||
| @@ -618,6 +618,7 @@ func CreateBranchProtection(ctx *context.APIContext) { | |||||||
| 	protectBranch = &git_model.ProtectedBranch{ | 	protectBranch = &git_model.ProtectedBranch{ | ||||||
| 		RepoID:                        ctx.Repo.Repository.ID, | 		RepoID:                        ctx.Repo.Repository.ID, | ||||||
| 		RuleName:                      ruleName, | 		RuleName:                      ruleName, | ||||||
|  | 		Priority:                      form.Priority, | ||||||
| 		CanPush:                       form.EnablePush, | 		CanPush:                       form.EnablePush, | ||||||
| 		EnableWhitelist:               form.EnablePush && form.EnablePushWhitelist, | 		EnableWhitelist:               form.EnablePush && form.EnablePushWhitelist, | ||||||
| 		WhitelistDeployKeys:           form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys, | 		WhitelistDeployKeys:           form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys, | ||||||
| @@ -640,7 +641,7 @@ func CreateBranchProtection(ctx *context.APIContext) { | |||||||
| 		BlockAdminMergeOverride:       form.BlockAdminMergeOverride, | 		BlockAdminMergeOverride:       form.BlockAdminMergeOverride, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ | 	if err := git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ | ||||||
| 		UserIDs:          whitelistUsers, | 		UserIDs:          whitelistUsers, | ||||||
| 		TeamIDs:          whitelistTeams, | 		TeamIDs:          whitelistTeams, | ||||||
| 		ForcePushUserIDs: forcePushAllowlistUsers, | 		ForcePushUserIDs: forcePushAllowlistUsers, | ||||||
| @@ -649,14 +650,13 @@ func CreateBranchProtection(ctx *context.APIContext) { | |||||||
| 		MergeTeamIDs:     mergeWhitelistTeams, | 		MergeTeamIDs:     mergeWhitelistTeams, | ||||||
| 		ApprovalsUserIDs: approvalsWhitelistUsers, | 		ApprovalsUserIDs: approvalsWhitelistUsers, | ||||||
| 		ApprovalsTeamIDs: approvalsWhitelistTeams, | 		ApprovalsTeamIDs: approvalsWhitelistTeams, | ||||||
| 	}) | 	}); err != nil { | ||||||
| 	if err != nil { |  | ||||||
| 		ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err) | 		ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if isBranchExist { | 	if isBranchExist { | ||||||
| 		if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, ruleName); err != nil { | 		if err := pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, ruleName); err != nil { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err) | 			ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -796,6 +796,10 @@ func EditBranchProtection(ctx *context.APIContext) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if form.Priority != nil { | ||||||
|  | 		protectBranch.Priority = *form.Priority | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if form.EnableMergeWhitelist != nil { | 	if form.EnableMergeWhitelist != nil { | ||||||
| 		protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist | 		protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist | ||||||
| 	} | 	} | ||||||
| @@ -1080,3 +1084,47 @@ func DeleteBranchProtection(ctx *context.APIContext) { | |||||||
|  |  | ||||||
| 	ctx.Status(http.StatusNoContent) | 	ctx.Status(http.StatusNoContent) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpdateBranchProtectionPriories updates the priorities of branch protections for a repo | ||||||
|  | func UpdateBranchProtectionPriories(ctx *context.APIContext) { | ||||||
|  | 	// swagger:operation POST /repos/{owner}/{repo}/branch_protections/priority repository repoUpdateBranchProtectionPriories | ||||||
|  | 	// --- | ||||||
|  | 	// summary: Update the priorities of branch protections for a repository. | ||||||
|  | 	// consumes: | ||||||
|  | 	// - application/json | ||||||
|  | 	// produces: | ||||||
|  | 	// - application/json | ||||||
|  | 	// parameters: | ||||||
|  | 	// - name: owner | ||||||
|  | 	//   in: path | ||||||
|  | 	//   description: owner of the repo | ||||||
|  | 	//   type: string | ||||||
|  | 	//   required: true | ||||||
|  | 	// - name: repo | ||||||
|  | 	//   in: path | ||||||
|  | 	//   description: name of the repo | ||||||
|  | 	//   type: string | ||||||
|  | 	//   required: true | ||||||
|  | 	// - name: body | ||||||
|  | 	//   in: body | ||||||
|  | 	//   schema: | ||||||
|  | 	//     "$ref": "#/definitions/UpdateBranchProtectionPriories" | ||||||
|  | 	// responses: | ||||||
|  | 	//   "204": | ||||||
|  | 	//     "$ref": "#/responses/empty" | ||||||
|  | 	//   "404": | ||||||
|  | 	//     "$ref": "#/responses/notFound" | ||||||
|  | 	//   "422": | ||||||
|  | 	//     "$ref": "#/responses/validationError" | ||||||
|  | 	//   "423": | ||||||
|  | 	//     "$ref": "#/responses/repoArchivedError" | ||||||
|  | 	form := web.GetForm(ctx).(*api.UpdateBranchProtectionPriories) | ||||||
|  | 	repo := ctx.Repo.Repository | ||||||
|  |  | ||||||
|  | 	if err := git_model.UpdateProtectBranchPriorities(ctx, repo, form.IDs); err != nil { | ||||||
|  | 		ctx.Error(http.StatusInternalServerError, "UpdateProtectBranchPriorities", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Status(http.StatusNoContent) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -146,6 +146,9 @@ type swaggerParameterBodies struct { | |||||||
| 	// in:body | 	// in:body | ||||||
| 	EditBranchProtectionOption api.EditBranchProtectionOption | 	EditBranchProtectionOption api.EditBranchProtectionOption | ||||||
|  |  | ||||||
|  | 	// in:body | ||||||
|  | 	UpdateBranchProtectionPriories api.UpdateBranchProtectionPriories | ||||||
|  |  | ||||||
| 	// in:body | 	// in:body | ||||||
| 	CreateOAuth2ApplicationOptions api.CreateOAuth2ApplicationOptions | 	CreateOAuth2ApplicationOptions api.CreateOAuth2ApplicationOptions | ||||||
|  |  | ||||||
|   | |||||||
| @@ -322,6 +322,16 @@ func DeleteProtectedBranchRulePost(ctx *context.Context) { | |||||||
| 	ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) | 	ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func UpdateBranchProtectionPriories(ctx *context.Context) { | ||||||
|  | 	form := web.GetForm(ctx).(*forms.ProtectBranchPriorityForm) | ||||||
|  | 	repo := ctx.Repo.Repository | ||||||
|  |  | ||||||
|  | 	if err := git_model.UpdateProtectBranchPriorities(ctx, repo, form.IDs); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateProtectBranchPriorities", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // RenameBranchPost responses for rename a branch | // RenameBranchPost responses for rename a branch | ||||||
| func RenameBranchPost(ctx *context.Context) { | func RenameBranchPost(ctx *context.Context) { | ||||||
| 	form := web.GetForm(ctx).(*forms.RenameBranchForm) | 	form := web.GetForm(ctx).(*forms.RenameBranchForm) | ||||||
|   | |||||||
| @@ -1081,6 +1081,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch). | 			m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch). | ||||||
| 				Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost) | 				Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost) | ||||||
| 			m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost) | 			m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost) | ||||||
|  | 			m.Post("/priority", web.Bind(forms.ProtectBranchPriorityForm{}), context.RepoMustNotBeArchived(), repo_setting.UpdateBranchProtectionPriories) | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
| 		m.Group("/tags", func() { | 		m.Group("/tags", func() { | ||||||
|   | |||||||
| @@ -158,6 +158,7 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo | |||||||
| 	return &api.BranchProtection{ | 	return &api.BranchProtection{ | ||||||
| 		BranchName:                    branchName, | 		BranchName:                    branchName, | ||||||
| 		RuleName:                      bp.RuleName, | 		RuleName:                      bp.RuleName, | ||||||
|  | 		Priority:                      bp.Priority, | ||||||
| 		EnablePush:                    bp.CanPush, | 		EnablePush:                    bp.CanPush, | ||||||
| 		EnablePushWhitelist:           bp.EnableWhitelist, | 		EnablePushWhitelist:           bp.EnableWhitelist, | ||||||
| 		PushWhitelistUsernames:        pushWhitelistUsernames, | 		PushWhitelistUsernames:        pushWhitelistUsernames, | ||||||
|   | |||||||
| @@ -228,6 +228,10 @@ func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) bin | |||||||
| 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type ProtectBranchPriorityForm struct { | ||||||
|  | 	IDs []int64 | ||||||
|  | } | ||||||
|  |  | ||||||
| //  __      __      ___.   .__                   __ | //  __      __      ___.   .__                   __ | ||||||
| // /  \    /  \ ____\_ |__ |  |__   ____   ____ |  | __ | // /  \    /  \ ____\_ |__ |  |__   ____   ____ |  | __ | ||||||
| // \   \/\/   // __ \| __ \|  |  \ /  _ \ /  _ \|  |/ / | // \   \/\/   // __ \| __ \|  |  \ /  _ \ /  _ \|  |/ / | ||||||
|   | |||||||
| @@ -37,9 +37,12 @@ | |||||||
| 			</h4> | 			</h4> | ||||||
|  |  | ||||||
| 			<div class="ui attached segment"> | 			<div class="ui attached segment"> | ||||||
| 				<div class="flex-list"> | 				<div class="flex-list" id="protected-branches-list" data-update-priority-url="{{$.Repository.Link}}/settings/branches/priority"> | ||||||
| 					{{range .ProtectedBranches}} | 					{{range .ProtectedBranches}} | ||||||
| 						<div class="flex-item tw-items-center"> | 						<div class="flex-item tw-items-center item" data-id="{{.ID}}" > | ||||||
|  | 							<div class="drag-handle tw-cursor-grab"> | ||||||
|  | 								{{svg "octicon-grabber" 16}} | ||||||
|  | 							</div> | ||||||
| 							<div class="flex-item-main"> | 							<div class="flex-item-main"> | ||||||
| 								<div class="flex-item-title"> | 								<div class="flex-item-title"> | ||||||
| 									<div class="ui basic primary label">{{.RuleName}}</div> | 									<div class="ui basic primary label">{{.RuleName}}</div> | ||||||
|   | |||||||
							
								
								
									
										82
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										82
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							| @@ -4666,6 +4666,58 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "/repos/{owner}/{repo}/branch_protections/priority": { | ||||||
|  |       "post": { | ||||||
|  |         "consumes": [ | ||||||
|  |           "application/json" | ||||||
|  |         ], | ||||||
|  |         "produces": [ | ||||||
|  |           "application/json" | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "repository" | ||||||
|  |         ], | ||||||
|  |         "summary": "Update the priorities of branch protections for a repository.", | ||||||
|  |         "operationId": "repoUpdateBranchProtectionPriories", | ||||||
|  |         "parameters": [ | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "owner of the repo", | ||||||
|  |             "name": "owner", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "name of the repo", | ||||||
|  |             "name": "repo", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "name": "body", | ||||||
|  |             "in": "body", | ||||||
|  |             "schema": { | ||||||
|  |               "$ref": "#/definitions/UpdateBranchProtectionPriories" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "responses": { | ||||||
|  |           "204": { | ||||||
|  |             "$ref": "#/responses/empty" | ||||||
|  |           }, | ||||||
|  |           "404": { | ||||||
|  |             "$ref": "#/responses/notFound" | ||||||
|  |           }, | ||||||
|  |           "422": { | ||||||
|  |             "$ref": "#/responses/validationError" | ||||||
|  |           }, | ||||||
|  |           "423": { | ||||||
|  |             "$ref": "#/responses/repoArchivedError" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "/repos/{owner}/{repo}/branch_protections/{name}": { |     "/repos/{owner}/{repo}/branch_protections/{name}": { | ||||||
|       "get": { |       "get": { | ||||||
|         "produces": [ |         "produces": [ | ||||||
| @@ -18874,6 +18926,11 @@ | |||||||
|           }, |           }, | ||||||
|           "x-go-name": "MergeWhitelistUsernames" |           "x-go-name": "MergeWhitelistUsernames" | ||||||
|         }, |         }, | ||||||
|  |         "priority": { | ||||||
|  |           "type": "integer", | ||||||
|  |           "format": "int64", | ||||||
|  |           "x-go-name": "Priority" | ||||||
|  |         }, | ||||||
|         "protected_file_patterns": { |         "protected_file_patterns": { | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "ProtectedFilePatterns" |           "x-go-name": "ProtectedFilePatterns" | ||||||
| @@ -19568,6 +19625,11 @@ | |||||||
|           }, |           }, | ||||||
|           "x-go-name": "MergeWhitelistUsernames" |           "x-go-name": "MergeWhitelistUsernames" | ||||||
|         }, |         }, | ||||||
|  |         "priority": { | ||||||
|  |           "type": "integer", | ||||||
|  |           "format": "int64", | ||||||
|  |           "x-go-name": "Priority" | ||||||
|  |         }, | ||||||
|         "protected_file_patterns": { |         "protected_file_patterns": { | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "ProtectedFilePatterns" |           "x-go-name": "ProtectedFilePatterns" | ||||||
| @@ -20800,6 +20862,11 @@ | |||||||
|           }, |           }, | ||||||
|           "x-go-name": "MergeWhitelistUsernames" |           "x-go-name": "MergeWhitelistUsernames" | ||||||
|         }, |         }, | ||||||
|  |         "priority": { | ||||||
|  |           "type": "integer", | ||||||
|  |           "format": "int64", | ||||||
|  |           "x-go-name": "Priority" | ||||||
|  |         }, | ||||||
|         "protected_file_patterns": { |         "protected_file_patterns": { | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "ProtectedFilePatterns" |           "x-go-name": "ProtectedFilePatterns" | ||||||
| @@ -24886,6 +24953,21 @@ | |||||||
|       }, |       }, | ||||||
|       "x-go-package": "code.gitea.io/gitea/modules/structs" |       "x-go-package": "code.gitea.io/gitea/modules/structs" | ||||||
|     }, |     }, | ||||||
|  |     "UpdateBranchProtectionPriories": { | ||||||
|  |       "description": "UpdateBranchProtectionPriories a list to update the branch protection rule priorities", | ||||||
|  |       "type": "object", | ||||||
|  |       "properties": { | ||||||
|  |         "ids": { | ||||||
|  |           "type": "array", | ||||||
|  |           "items": { | ||||||
|  |             "type": "integer", | ||||||
|  |             "format": "int64" | ||||||
|  |           }, | ||||||
|  |           "x-go-name": "IDs" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "x-go-package": "code.gitea.io/gitea/modules/structs" | ||||||
|  |     }, | ||||||
|     "UpdateFileOptions": { |     "UpdateFileOptions": { | ||||||
|       "description": "UpdateFileOptions options for updating files\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", |       "description": "UpdateFileOptions options for updating files\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", | ||||||
|       "type": "object", |       "type": "object", | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | func testAPICreateBranchProtection(t *testing.T, branchName string, expectedPriority, expectedHTTPStatus int) { | ||||||
| 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | ||||||
| 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections", &api.BranchProtection{ | 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections", &api.BranchProtection{ | ||||||
| 		RuleName: branchName, | 		RuleName: branchName, | ||||||
| @@ -60,6 +60,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP | |||||||
| 		var branchProtection api.BranchProtection | 		var branchProtection api.BranchProtection | ||||||
| 		DecodeJSON(t, resp, &branchProtection) | 		DecodeJSON(t, resp, &branchProtection) | ||||||
| 		assert.EqualValues(t, branchName, branchProtection.RuleName) | 		assert.EqualValues(t, branchName, branchProtection.RuleName) | ||||||
|  | 		assert.EqualValues(t, expectedPriority, branchProtection.Priority) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -189,13 +190,13 @@ func TestAPIBranchProtection(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	// Branch protection  on branch that not exist | 	// Branch protection  on branch that not exist | ||||||
| 	testAPICreateBranchProtection(t, "master/doesnotexist", http.StatusCreated) | 	testAPICreateBranchProtection(t, "master/doesnotexist", 1, http.StatusCreated) | ||||||
| 	// Get branch protection on branch that exist but not branch protection | 	// Get branch protection on branch that exist but not branch protection | ||||||
| 	testAPIGetBranchProtection(t, "master", http.StatusNotFound) | 	testAPIGetBranchProtection(t, "master", http.StatusNotFound) | ||||||
|  |  | ||||||
| 	testAPICreateBranchProtection(t, "master", http.StatusCreated) | 	testAPICreateBranchProtection(t, "master", 2, http.StatusCreated) | ||||||
| 	// Can only create once | 	// Can only create once | ||||||
| 	testAPICreateBranchProtection(t, "master", http.StatusForbidden) | 	testAPICreateBranchProtection(t, "master", 0, http.StatusForbidden) | ||||||
|  |  | ||||||
| 	// Can't delete a protected branch | 	// Can't delete a protected branch | ||||||
| 	testAPIDeleteBranch(t, "master", http.StatusForbidden) | 	testAPIDeleteBranch(t, "master", http.StatusForbidden) | ||||||
|   | |||||||
| @@ -196,7 +196,11 @@ async function initIssuePinSort() { | |||||||
|  |  | ||||||
|   createSortable(pinDiv, { |   createSortable(pinDiv, { | ||||||
|     group: 'shared', |     group: 'shared', | ||||||
|     onEnd: pinMoveEnd, // eslint-disable-line @typescript-eslint/no-misused-promises |     onEnd: (e) => { | ||||||
|  |       (async () => { | ||||||
|  |         await pinMoveEnd(e); | ||||||
|  |       })(); | ||||||
|  |     }, | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										71
									
								
								web_src/js/features/repo-settings-branches.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								web_src/js/features/repo-settings-branches.test.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | import {beforeEach, describe, expect, test, vi} from 'vitest'; | ||||||
|  | import {initRepoBranchesSettings} from './repo-settings-branches.ts'; | ||||||
|  | import {POST} from '../modules/fetch.ts'; | ||||||
|  | import {createSortable} from '../modules/sortable.ts'; | ||||||
|  |  | ||||||
|  | vi.mock('../modules/fetch.ts', () => ({ | ||||||
|  |   POST: vi.fn(), | ||||||
|  | })); | ||||||
|  |  | ||||||
|  | vi.mock('../modules/sortable.ts', () => ({ | ||||||
|  |   createSortable: vi.fn(), | ||||||
|  | })); | ||||||
|  |  | ||||||
|  | describe('Repository Branch Settings', () => { | ||||||
|  |   beforeEach(() => { | ||||||
|  |     document.body.innerHTML = ` | ||||||
|  |       <div id="protected-branches-list" data-update-priority-url="some/repo/branches/priority"> | ||||||
|  |         <div class="flex-item tw-items-center item" data-id="1" > | ||||||
|  |           <div class="drag-handle"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="flex-item tw-items-center item" data-id="2" > | ||||||
|  |           <div class="drag-handle"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="flex-item tw-items-center item" data-id="3" > | ||||||
|  |           <div class="drag-handle"></div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     `; | ||||||
|  |  | ||||||
|  |     vi.clearAllMocks(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('should initialize sortable for protected branches list', () => { | ||||||
|  |     initRepoBranchesSettings(); | ||||||
|  |  | ||||||
|  |     expect(createSortable).toHaveBeenCalledWith( | ||||||
|  |       document.querySelector('#protected-branches-list'), | ||||||
|  |       expect.objectContaining({ | ||||||
|  |         handle: '.drag-handle', | ||||||
|  |         animation: 150, | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('should not initialize if protected branches list is not present', () => { | ||||||
|  |     document.body.innerHTML = ''; | ||||||
|  |  | ||||||
|  |     initRepoBranchesSettings(); | ||||||
|  |  | ||||||
|  |     expect(createSortable).not.toHaveBeenCalled(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('should post new order after sorting', async () => { | ||||||
|  |     vi.mocked(POST).mockResolvedValue({ok: true} as Response); | ||||||
|  |  | ||||||
|  |     // Mock createSortable to capture and execute the onEnd callback | ||||||
|  |     vi.mocked(createSortable).mockImplementation((_el, options) => { | ||||||
|  |       options.onEnd(); | ||||||
|  |       return {destroy: vi.fn()}; | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     initRepoBranchesSettings(); | ||||||
|  |  | ||||||
|  |     expect(POST).toHaveBeenCalledWith( | ||||||
|  |       'some/repo/branches/priority', | ||||||
|  |       expect.objectContaining({ | ||||||
|  |         data: {ids: [1, 2, 3]}, | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										32
									
								
								web_src/js/features/repo-settings-branches.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web_src/js/features/repo-settings-branches.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | import {createSortable} from '../modules/sortable.ts'; | ||||||
|  | import {POST} from '../modules/fetch.ts'; | ||||||
|  | import {showErrorToast} from '../modules/toast.ts'; | ||||||
|  | import {queryElemChildren} from '../utils/dom.ts'; | ||||||
|  |  | ||||||
|  | export function initRepoBranchesSettings() { | ||||||
|  |   const protectedBranchesList = document.querySelector('#protected-branches-list'); | ||||||
|  |   if (!protectedBranchesList) return; | ||||||
|  |  | ||||||
|  |   createSortable(protectedBranchesList, { | ||||||
|  |     handle: '.drag-handle', | ||||||
|  |     animation: 150, | ||||||
|  |  | ||||||
|  |     onEnd: () => { | ||||||
|  |       (async () => { | ||||||
|  |         const itemElems = queryElemChildren(protectedBranchesList, '.item[data-id]'); | ||||||
|  |         const itemIds = Array.from(itemElems, (el) => parseInt(el.getAttribute('data-id'))); | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |           await POST(protectedBranchesList.getAttribute('data-update-priority-url'), { | ||||||
|  |             data: { | ||||||
|  |               ids: itemIds, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         } catch (err) { | ||||||
|  |           const errorMessage = String(err); | ||||||
|  |           showErrorToast(`Failed to update branch protection rule priority:, error: ${errorMessage}`); | ||||||
|  |         } | ||||||
|  |       })(); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
| @@ -3,6 +3,7 @@ import {minimatch} from 'minimatch'; | |||||||
| import {createMonaco} from './codeeditor.ts'; | import {createMonaco} from './codeeditor.ts'; | ||||||
| import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts'; | import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts'; | ||||||
| import {POST} from '../modules/fetch.ts'; | import {POST} from '../modules/fetch.ts'; | ||||||
|  | import {initRepoBranchesSettings} from './repo-settings-branches.ts'; | ||||||
|  |  | ||||||
| const {appSubUrl, csrfToken} = window.config; | const {appSubUrl, csrfToken} = window.config; | ||||||
|  |  | ||||||
| @@ -154,4 +155,5 @@ export function initRepoSettings() { | |||||||
|   initRepoSettingsCollaboration(); |   initRepoSettingsCollaboration(); | ||||||
|   initRepoSettingsSearchTeamBox(); |   initRepoSettingsSearchTeamBox(); | ||||||
|   initRepoSettingsGitHook(); |   initRepoSettingsGitHook(); | ||||||
|  |   initRepoBranchesSettings(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ import octiconGitCommit from '../../public/assets/img/svg/octicon-git-commit.svg | |||||||
| import octiconGitMerge from '../../public/assets/img/svg/octicon-git-merge.svg'; | import octiconGitMerge from '../../public/assets/img/svg/octicon-git-merge.svg'; | ||||||
| import octiconGitPullRequest from '../../public/assets/img/svg/octicon-git-pull-request.svg'; | import octiconGitPullRequest from '../../public/assets/img/svg/octicon-git-pull-request.svg'; | ||||||
| import octiconGitPullRequestDraft from '../../public/assets/img/svg/octicon-git-pull-request-draft.svg'; | import octiconGitPullRequestDraft from '../../public/assets/img/svg/octicon-git-pull-request-draft.svg'; | ||||||
|  | import octiconGrabber from '../../public/assets/img/svg/octicon-grabber.svg'; | ||||||
| import octiconHeading from '../../public/assets/img/svg/octicon-heading.svg'; | import octiconHeading from '../../public/assets/img/svg/octicon-heading.svg'; | ||||||
| import octiconHorizontalRule from '../../public/assets/img/svg/octicon-horizontal-rule.svg'; | import octiconHorizontalRule from '../../public/assets/img/svg/octicon-horizontal-rule.svg'; | ||||||
| import octiconImage from '../../public/assets/img/svg/octicon-image.svg'; | import octiconImage from '../../public/assets/img/svg/octicon-image.svg'; | ||||||
| @@ -107,6 +108,7 @@ const svgs = { | |||||||
|   'octicon-git-merge': octiconGitMerge, |   'octicon-git-merge': octiconGitMerge, | ||||||
|   'octicon-git-pull-request': octiconGitPullRequest, |   'octicon-git-pull-request': octiconGitPullRequest, | ||||||
|   'octicon-git-pull-request-draft': octiconGitPullRequestDraft, |   'octicon-git-pull-request-draft': octiconGitPullRequestDraft, | ||||||
|  |   'octicon-grabber': octiconGrabber, | ||||||
|   'octicon-heading': octiconHeading, |   'octicon-heading': octiconHeading, | ||||||
|   'octicon-horizontal-rule': octiconHorizontalRule, |   'octicon-horizontal-rule': octiconHorizontalRule, | ||||||
|   'octicon-image': octiconImage, |   'octicon-image': octiconImage, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user