mirror of https://github.com/harness/drone.git
feat: [CODE-2359]: PR source branch delete/restore API (#2690)
* passed 409 for open PRs for restore branch API * addressed review comments * fixed lint issue * feat: [CODE-2359]: PR source branch delete/restore APICODE-2402
parent
327c5ed284
commit
93b642b2ba
|
@ -20,7 +20,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
apiauth "github.com/harness/gitness/app/api/auth"
|
|
||||||
"github.com/harness/gitness/app/auth"
|
"github.com/harness/gitness/app/auth"
|
||||||
"github.com/harness/gitness/app/services/protection"
|
"github.com/harness/gitness/app/services/protection"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
@ -44,14 +43,9 @@ func (c *Controller) ListChecks(
|
||||||
return types.PullReqChecks{}, fmt.Errorf("failed to find pull request by number: %w", err)
|
return types.PullReqChecks{}, fmt.Errorf("failed to find pull request by number: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, repo)
|
protectionRules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.PullReqChecks{}, fmt.Errorf("failed to determine if user is repo owner: %w", err)
|
return types.PullReqChecks{}, fmt.Errorf("failed to fetch rules: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
return types.PullReqChecks{}, fmt.Errorf("failed to fetch protection rules for the repository: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reqChecks, err := protectionRules.RequiredChecks(ctx, protection.RequiredChecksInput{
|
reqChecks, err := protectionRules.RequiredChecks(ctx, protection.RequiredChecksInput{
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
apiauth "github.com/harness/gitness/app/api/auth"
|
|
||||||
"github.com/harness/gitness/app/api/controller"
|
"github.com/harness/gitness/app/api/controller"
|
||||||
"github.com/harness/gitness/app/api/usererror"
|
"github.com/harness/gitness/app/api/usererror"
|
||||||
"github.com/harness/gitness/app/auth"
|
"github.com/harness/gitness/app/auth"
|
||||||
|
@ -112,14 +111,9 @@ func (c *Controller) CommentApplySuggestions(
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify branch rules
|
// verify branch rules
|
||||||
isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, repo)
|
protectionRules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CommentApplySuggestionsOutput{}, nil, fmt.Errorf("failed to determine if user is repo owner: %w", err)
|
return CommentApplySuggestionsOutput{}, nil, fmt.Errorf("failed to fetch rules: %w", err)
|
||||||
}
|
|
||||||
protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
return CommentApplySuggestionsOutput{}, nil, fmt.Errorf(
|
|
||||||
"failed to fetch protection rules for the repository: %w", err)
|
|
||||||
}
|
}
|
||||||
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
||||||
Actor: &session.Principal,
|
Actor: &session.Principal,
|
||||||
|
|
|
@ -199,6 +199,24 @@ func (c *Controller) getRepoCheckAccess(ctx context.Context,
|
||||||
return repo, nil
|
return repo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) fetchRules(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
repo *types.Repository,
|
||||||
|
) (protection.Protection, bool, error) {
|
||||||
|
isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to determine if user is repo owner: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to fetch protection rules for the repository: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return protectionRules, isRepoOwner, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) getCommentForPR(
|
func (c *Controller) getCommentForPR(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
pr *types.PullReq,
|
pr *types.PullReq,
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pullreq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller"
|
||||||
|
"github.com/harness/gitness/app/api/usererror"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/app/services/protection"
|
||||||
|
"github.com/harness/gitness/git"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteBranch deletes the source branch of a PR.
|
||||||
|
func (c *Controller) DeleteBranch(ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
repoRef string,
|
||||||
|
pullreqNum int64,
|
||||||
|
bypassRules bool,
|
||||||
|
dryRunRules bool,
|
||||||
|
) (types.DeleteBranchOutput, []types.RuleViolations, error) {
|
||||||
|
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush)
|
||||||
|
if err != nil {
|
||||||
|
return types.DeleteBranchOutput{}, nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, err := c.pullreqStore.FindByNumber(ctx, repo.ID, pullreqNum)
|
||||||
|
if err != nil {
|
||||||
|
return types.DeleteBranchOutput{}, nil, fmt.Errorf("failed to get pull request by number: %w", err)
|
||||||
|
}
|
||||||
|
branchName := pr.SourceBranch
|
||||||
|
|
||||||
|
// make sure user isn't deleting the default branch
|
||||||
|
if branchName == repo.DefaultBranch {
|
||||||
|
return types.DeleteBranchOutput{}, nil, usererror.ErrDefaultBranchCantBeDeleted
|
||||||
|
}
|
||||||
|
|
||||||
|
rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
|
||||||
|
if err != nil {
|
||||||
|
return types.DeleteBranchOutput{}, nil, fmt.Errorf("failed to fetch rules: %w", err)
|
||||||
|
}
|
||||||
|
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
||||||
|
Actor: &session.Principal,
|
||||||
|
AllowBypass: bypassRules,
|
||||||
|
IsRepoOwner: isRepoOwner,
|
||||||
|
Repo: repo,
|
||||||
|
RefAction: protection.RefActionDelete,
|
||||||
|
RefType: protection.RefTypeBranch,
|
||||||
|
RefNames: []string{branchName},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return types.DeleteBranchOutput{}, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRunRules {
|
||||||
|
return types.DeleteBranchOutput{
|
||||||
|
DryRunRulesOutput: types.DryRunRulesOutput{
|
||||||
|
DryRunRules: true,
|
||||||
|
RuleViolations: violations,
|
||||||
|
},
|
||||||
|
}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if protection.IsCritical(violations) {
|
||||||
|
return types.DeleteBranchOutput{}, violations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
writeParams, err := controller.CreateRPCInternalWriteParams(ctx, c.urlProvider, session, repo)
|
||||||
|
if err != nil {
|
||||||
|
return types.DeleteBranchOutput{}, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.git.DeleteBranch(ctx, &git.DeleteBranchParams{
|
||||||
|
WriteParams: writeParams,
|
||||||
|
BranchName: branchName,
|
||||||
|
SHA: pr.SourceSHA,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return types.DeleteBranchOutput{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = func() error {
|
||||||
|
if pr, err = c.pullreqStore.UpdateActivitySeq(ctx, pr); err != nil {
|
||||||
|
return fmt.Errorf("failed to update pull request activity sequence: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := c.activityStore.CreateWithPayload(ctx, pr, session.Principal.ID,
|
||||||
|
&types.PullRequestActivityPayloadBranchDelete{SHA: pr.SourceSHA}, nil)
|
||||||
|
return err
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Err(err).Msgf("failed to write pull request activity for successful branch delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.DeleteBranchOutput{
|
||||||
|
DryRunRulesOutput: types.DryRunRulesOutput{
|
||||||
|
RuleViolations: violations,
|
||||||
|
}}, nil, nil
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
apiauth "github.com/harness/gitness/app/api/auth"
|
|
||||||
"github.com/harness/gitness/app/api/controller"
|
"github.com/harness/gitness/app/api/controller"
|
||||||
"github.com/harness/gitness/app/api/usererror"
|
"github.com/harness/gitness/app/api/usererror"
|
||||||
"github.com/harness/gitness/app/auth"
|
"github.com/harness/gitness/app/auth"
|
||||||
|
@ -182,9 +181,9 @@ func (c *Controller) Merge(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, targetRepo)
|
protectionRules, isRepoOwner, err := c.fetchRules(ctx, session, targetRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to determine if user is repo owner: %w", err)
|
return nil, nil, fmt.Errorf("failed to fetch rules: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkResults, err := c.checkStore.ListResults(ctx, targetRepo.ID, pr.SourceSHA)
|
checkResults, err := c.checkStore.ListResults(ctx, targetRepo.ID, pr.SourceSHA)
|
||||||
|
@ -192,11 +191,6 @@ func (c *Controller) Merge(
|
||||||
return nil, nil, fmt.Errorf("failed to list status checks: %w", err)
|
return nil, nil, fmt.Errorf("failed to list status checks: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
protectionRules, err := c.protectionManager.ForRepository(ctx, targetRepo.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to fetch protection rules for the repository: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
codeOwnerWithApproval, err := c.codeOwners.Evaluate(ctx, sourceRepo, pr, reviewers)
|
codeOwnerWithApproval, err := c.codeOwners.Evaluate(ctx, sourceRepo, pr, reviewers)
|
||||||
// check for error and ignore if it is codeowners file not found else throw error
|
// check for error and ignore if it is codeowners file not found else throw error
|
||||||
if err != nil && !errors.Is(err, codeowners.ErrNotFound) {
|
if err != nil && !errors.Is(err, codeowners.ErrNotFound) {
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pullreq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/app/services/instrument"
|
||||||
|
"github.com/harness/gitness/app/services/protection"
|
||||||
|
"github.com/harness/gitness/errors"
|
||||||
|
"github.com/harness/gitness/git"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RestoreBranchInput used for branch restoration apis.
|
||||||
|
type RestoreBranchInput struct {
|
||||||
|
DryRunRules bool `json:"dry_run_rules"`
|
||||||
|
BypassRules bool `json:"bypass_rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreBranch restores branch for the merged/closed PR.
|
||||||
|
func (c *Controller) RestoreBranch(ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
repoRef string,
|
||||||
|
pullreqNum int64,
|
||||||
|
in *RestoreBranchInput,
|
||||||
|
) (types.CreateBranchOutput, []types.RuleViolations, error) {
|
||||||
|
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush)
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, err := c.pullreqStore.FindByNumber(ctx, repo.ID, pullreqNum)
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to get pull request by number: %w", err)
|
||||||
|
}
|
||||||
|
if pr.State == enum.PullReqStateOpen {
|
||||||
|
return types.CreateBranchOutput{}, nil, errors.Conflict("source branch %q already exists", pr.SourceBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to fetch rules: %w", err)
|
||||||
|
}
|
||||||
|
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
||||||
|
Actor: &session.Principal,
|
||||||
|
AllowBypass: in.BypassRules,
|
||||||
|
IsRepoOwner: isRepoOwner,
|
||||||
|
Repo: repo,
|
||||||
|
RefAction: protection.RefActionCreate,
|
||||||
|
RefType: protection.RefTypeBranch,
|
||||||
|
RefNames: []string{pr.SourceBranch},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.DryRunRules {
|
||||||
|
return types.CreateBranchOutput{
|
||||||
|
DryRunRulesOutput: types.DryRunRulesOutput{
|
||||||
|
DryRunRules: true,
|
||||||
|
RuleViolations: violations,
|
||||||
|
},
|
||||||
|
}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if protection.IsCritical(violations) {
|
||||||
|
return types.CreateBranchOutput{}, violations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
writeParams, err := controller.CreateRPCInternalWriteParams(ctx, c.urlProvider, session, repo)
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcOut, err := c.git.CreateBranch(ctx, &git.CreateBranchParams{
|
||||||
|
WriteParams: writeParams,
|
||||||
|
BranchName: pr.SourceBranch,
|
||||||
|
Target: pr.SourceSHA,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, err
|
||||||
|
}
|
||||||
|
branch, err := controller.MapBranch(rpcOut.Branch)
|
||||||
|
if err != nil {
|
||||||
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to map branch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = func() error {
|
||||||
|
if pr, err = c.pullreqStore.UpdateActivitySeq(ctx, pr); err != nil {
|
||||||
|
return fmt.Errorf("failed to update pull request activity sequence: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := c.activityStore.CreateWithPayload(ctx, pr, session.Principal.ID,
|
||||||
|
&types.PullRequestActivityPayloadBranchRestore{SHA: pr.SourceSHA}, nil)
|
||||||
|
return err
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Err(err).Msgf("failed to write pull request activity for successful branch restore")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.instrumentation.Track(ctx, instrument.Event{
|
||||||
|
Type: instrument.EventTypeCreateBranch,
|
||||||
|
Principal: session.Principal.ToPrincipalInfo(),
|
||||||
|
Path: repo.Path,
|
||||||
|
Properties: map[instrument.Property]any{
|
||||||
|
instrument.PropertyRepositoryID: repo.ID,
|
||||||
|
instrument.PropertyRepositoryName: repo.Identifier,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to insert instrumentation record for create branch operation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.CreateBranchOutput{
|
||||||
|
Branch: branch,
|
||||||
|
}, nil, nil
|
||||||
|
}
|
|
@ -102,7 +102,7 @@ func (c *Controller) CreateBranch(ctx context.Context,
|
||||||
return types.CreateBranchOutput{}, nil, err
|
return types.CreateBranchOutput{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
branch, err := mapBranch(rpcOut.Branch)
|
branch, err := controller.MapBranch(rpcOut.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to map branch: %w", err)
|
return types.CreateBranchOutput{}, nil, fmt.Errorf("failed to map branch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller"
|
||||||
"github.com/harness/gitness/app/auth"
|
"github.com/harness/gitness/app/auth"
|
||||||
"github.com/harness/gitness/git"
|
"github.com/harness/gitness/git"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
@ -43,7 +44,7 @@ func (c *Controller) GetBranch(ctx context.Context,
|
||||||
return nil, fmt.Errorf("failed to get branch: %w", err)
|
return nil, fmt.Errorf("failed to get branch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
branch, err := mapBranch(rpcOut.Branch)
|
branch, err := controller.MapBranch(rpcOut.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to map branch: %w", err)
|
return nil, fmt.Errorf("failed to map branch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (c *Controller) ListBranches(ctx context.Context,
|
||||||
|
|
||||||
branches := make([]types.Branch, len(rpcOut.Branches))
|
branches := make([]types.Branch, len(rpcOut.Branches))
|
||||||
for i := range rpcOut.Branches {
|
for i := range rpcOut.Branches {
|
||||||
branches[i], err = mapBranch(rpcOut.Branches[i])
|
branches[i], err = controller.MapBranch(rpcOut.Branches[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to map branch: %w", err)
|
return nil, fmt.Errorf("failed to map branch: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -88,19 +88,3 @@ func mapToRPCSortOrder(o enum.Order) git.SortOrder {
|
||||||
return git.SortOrderDefault
|
return git.SortOrderDefault
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapBranch(b git.Branch) (types.Branch, error) {
|
|
||||||
var commit *types.Commit
|
|
||||||
if b.Commit != nil {
|
|
||||||
var err error
|
|
||||||
commit, err = controller.MapCommit(b.Commit)
|
|
||||||
if err != nil {
|
|
||||||
return types.Branch{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types.Branch{
|
|
||||||
Name: b.Name,
|
|
||||||
SHA: b.SHA.String(),
|
|
||||||
Commit: commit,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
apiauth "github.com/harness/gitness/app/api/auth"
|
|
||||||
"github.com/harness/gitness/app/api/controller"
|
"github.com/harness/gitness/app/api/controller"
|
||||||
"github.com/harness/gitness/app/api/usererror"
|
"github.com/harness/gitness/app/api/usererror"
|
||||||
"github.com/harness/gitness/app/auth"
|
"github.com/harness/gitness/app/auth"
|
||||||
|
@ -71,14 +70,9 @@ func (c *Controller) Rebase(
|
||||||
return nil, nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
return nil, nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, repo)
|
protectionRules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to determine if user is repo owner: %w", err)
|
return nil, nil, fmt.Errorf("failed to fetch rules: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to fetch protection rules for the repository: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
||||||
|
|
|
@ -79,6 +79,22 @@ func CreateRPCInternalWriteParams(
|
||||||
return createRPCWriteParams(ctx, urlProvider, session, repo, true)
|
return createRPCWriteParams(ctx, urlProvider, session, repo, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapBranch(b git.Branch) (types.Branch, error) {
|
||||||
|
var commit *types.Commit
|
||||||
|
if b.Commit != nil {
|
||||||
|
var err error
|
||||||
|
commit, err = MapCommit(b.Commit)
|
||||||
|
if err != nil {
|
||||||
|
return types.Branch{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.Branch{
|
||||||
|
Name: b.Name,
|
||||||
|
SHA: b.SHA.String(),
|
||||||
|
Commit: commit,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func MapCommit(c *git.Commit) (*types.Commit, error) {
|
func MapCommit(c *git.Commit) (*types.Commit, error) {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil, fmt.Errorf("commit is nil")
|
return nil, fmt.Errorf("commit is nil")
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pullreq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleDeleteBranch deletes the source branch of a PR.
|
||||||
|
func HandleDeleteBranch(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
repoRef, err := request.GetRepoRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pullreqNumber, err := request.GetPullReqNumberFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bypassRules, err := request.ParseBypassRulesFromQuery(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dryRunRules, err := request.ParseDryRunRulesFromQuery(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out, violations, err := pullreqCtrl.DeleteBranch(ctx, session, repoRef, pullreqNumber, bypassRules, dryRunRules)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if violations != nil {
|
||||||
|
render.Violations(w, violations)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, out)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pullreq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleRestoreBranch writes json-encoded branch information to the http response body.
|
||||||
|
func HandleRestoreBranch(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
repoRef, err := request.GetRepoRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pullreqNumber, err := request.GetPullReqNumberFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := new(pullreq.RestoreBranchInput)
|
||||||
|
err = json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out, violations, err := pullreqCtrl.RestoreBranch(ctx, session, repoRef, pullreqNumber, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if violations != nil {
|
||||||
|
render.Violations(w, violations)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusCreated, out)
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,7 @@ func HandleCreateBranch(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
branch, violations, err := repoCtrl.CreateBranch(ctx, session, repoRef, in)
|
out, violations, err := repoCtrl.CreateBranch(ctx, session, repoRef, in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.TranslatedUserError(ctx, w, err)
|
render.TranslatedUserError(ctx, w, err)
|
||||||
return
|
return
|
||||||
|
@ -51,6 +51,6 @@ func HandleCreateBranch(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
render.JSON(w, http.StatusCreated, branch)
|
render.JSON(w, http.StatusCreated, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -702,6 +702,34 @@ func pullReqOperations(reflector *openapi3.Reflector) {
|
||||||
_ = reflector.SetJSONResponse(&opMetaData, new(usererror.Error), http.StatusNotFound)
|
_ = reflector.SetJSONResponse(&opMetaData, new(usererror.Error), http.StatusNotFound)
|
||||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/pullreq/{pullreq_number}/metadata", opMetaData)
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/pullreq/{pullreq_number}/metadata", opMetaData)
|
||||||
|
|
||||||
|
opRestoreBranch := openapi3.Operation{}
|
||||||
|
opRestoreBranch.WithTags("pullreq")
|
||||||
|
opRestoreBranch.WithMapOfAnything(map[string]interface{}{"operationId": "restorePullReqSourceBranch"})
|
||||||
|
_ = reflector.SetRequest(&opRestoreBranch, struct {
|
||||||
|
pullReqRequest
|
||||||
|
pullreq.RestoreBranchInput
|
||||||
|
}{}, http.MethodPost)
|
||||||
|
_ = reflector.SetJSONResponse(&opRestoreBranch, new(types.CreateBranchOutput), http.StatusCreated)
|
||||||
|
_ = reflector.SetJSONResponse(&opRestoreBranch, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opRestoreBranch, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opRestoreBranch, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opRestoreBranch, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opRestoreBranch, new(types.RulesViolations), http.StatusUnprocessableEntity)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/pullreq/{pullreq_number}/branch", opRestoreBranch)
|
||||||
|
|
||||||
|
opDeleteBranch := openapi3.Operation{}
|
||||||
|
opDeleteBranch.WithTags("pullreq")
|
||||||
|
opDeleteBranch.WithMapOfAnything(map[string]interface{}{"operationId": "deletePullReqSourceBranch"})
|
||||||
|
opDeleteBranch.WithParameters(queryParameterBypassRules, queryParameterDryRunRules)
|
||||||
|
_ = reflector.SetRequest(&opDeleteBranch, new(pullReqRequest), http.MethodDelete)
|
||||||
|
_ = reflector.SetJSONResponse(&opDeleteBranch, new(types.DeleteBranchOutput), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opDeleteBranch, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opDeleteBranch, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opDeleteBranch, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opDeleteBranch, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.SetJSONResponse(&opDeleteBranch, new(types.RulesViolations), http.StatusUnprocessableEntity)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodDelete, "/repos/{repo_ref}/pullreq/{pullreq_number}/branch", opDeleteBranch)
|
||||||
|
|
||||||
fileViewAdd := openapi3.Operation{}
|
fileViewAdd := openapi3.Operation{}
|
||||||
fileViewAdd.WithTags("pullreq")
|
fileViewAdd.WithTags("pullreq")
|
||||||
fileViewAdd.WithMapOfAnything(map[string]interface{}{"operationId": "fileViewAddPullReq"})
|
fileViewAdd.WithMapOfAnything(map[string]interface{}{"operationId": "fileViewAddPullReq"})
|
||||||
|
|
|
@ -649,6 +649,10 @@ func SetupPullReq(r chi.Router, pullreqCtrl *pullreq.Controller) {
|
||||||
r.Post("/merge", handlerpullreq.HandleMerge(pullreqCtrl))
|
r.Post("/merge", handlerpullreq.HandleMerge(pullreqCtrl))
|
||||||
r.Get("/commits", handlerpullreq.HandleCommits(pullreqCtrl))
|
r.Get("/commits", handlerpullreq.HandleCommits(pullreqCtrl))
|
||||||
r.Get("/metadata", handlerpullreq.HandleMetadata(pullreqCtrl))
|
r.Get("/metadata", handlerpullreq.HandleMetadata(pullreqCtrl))
|
||||||
|
r.Route("/branch", func(r chi.Router) {
|
||||||
|
r.Post("/", handlerpullreq.HandleRestoreBranch(pullreqCtrl))
|
||||||
|
r.Delete("/", handlerpullreq.HandleDeleteBranch(pullreqCtrl))
|
||||||
|
})
|
||||||
|
|
||||||
r.Route("/file-views", func(r chi.Router) {
|
r.Route("/file-views", func(r chi.Router) {
|
||||||
r.Put("/", handlerpullreq.HandleFileViewAdd(pullreqCtrl))
|
r.Put("/", handlerpullreq.HandleFileViewAdd(pullreqCtrl))
|
||||||
|
|
|
@ -87,6 +87,7 @@ const (
|
||||||
PullReqActivityTypeReviewerDelete PullReqActivityType = "reviewer-delete"
|
PullReqActivityTypeReviewerDelete PullReqActivityType = "reviewer-delete"
|
||||||
PullReqActivityTypeBranchUpdate PullReqActivityType = "branch-update"
|
PullReqActivityTypeBranchUpdate PullReqActivityType = "branch-update"
|
||||||
PullReqActivityTypeBranchDelete PullReqActivityType = "branch-delete"
|
PullReqActivityTypeBranchDelete PullReqActivityType = "branch-delete"
|
||||||
|
PullReqActivityTypeBranchRestore PullReqActivityType = "branch-restore"
|
||||||
PullReqActivityTypeMerge PullReqActivityType = "merge"
|
PullReqActivityTypeMerge PullReqActivityType = "merge"
|
||||||
PullReqActivityTypeLabelModify PullReqActivityType = "label-modify"
|
PullReqActivityTypeLabelModify PullReqActivityType = "label-modify"
|
||||||
)
|
)
|
||||||
|
@ -100,6 +101,7 @@ var pullReqActivityTypes = sortEnum([]PullReqActivityType{
|
||||||
PullReqActivityTypeReviewerDelete,
|
PullReqActivityTypeReviewerDelete,
|
||||||
PullReqActivityTypeBranchUpdate,
|
PullReqActivityTypeBranchUpdate,
|
||||||
PullReqActivityTypeBranchDelete,
|
PullReqActivityTypeBranchDelete,
|
||||||
|
PullReqActivityTypeBranchRestore,
|
||||||
PullReqActivityTypeMerge,
|
PullReqActivityTypeMerge,
|
||||||
PullReqActivityTypeLabelModify,
|
PullReqActivityTypeLabelModify,
|
||||||
})
|
})
|
||||||
|
|
|
@ -60,6 +60,7 @@ var allPullReqActivityPayloads = func(
|
||||||
func() PullReqActivityPayload { return &PullRequestActivityPayloadReviewSubmit{} },
|
func() PullReqActivityPayload { return &PullRequestActivityPayloadReviewSubmit{} },
|
||||||
func() PullReqActivityPayload { return &PullRequestActivityPayloadBranchUpdate{} },
|
func() PullReqActivityPayload { return &PullRequestActivityPayloadBranchUpdate{} },
|
||||||
func() PullReqActivityPayload { return &PullRequestActivityPayloadBranchDelete{} },
|
func() PullReqActivityPayload { return &PullRequestActivityPayloadBranchDelete{} },
|
||||||
|
func() PullReqActivityPayload { return &PullRequestActivityPayloadBranchRestore{} },
|
||||||
})
|
})
|
||||||
|
|
||||||
// newPayloadForActivity returns a new payload instance for the requested activity type.
|
// newPayloadForActivity returns a new payload instance for the requested activity type.
|
||||||
|
@ -169,6 +170,14 @@ func (a *PullRequestActivityPayloadBranchDelete) ActivityType() enum.PullReqActi
|
||||||
return enum.PullReqActivityTypeBranchDelete
|
return enum.PullReqActivityTypeBranchDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PullRequestActivityPayloadBranchRestore struct {
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PullRequestActivityPayloadBranchRestore) ActivityType() enum.PullReqActivityType {
|
||||||
|
return enum.PullReqActivityTypeBranchRestore
|
||||||
|
}
|
||||||
|
|
||||||
type PullRequestActivityLabel struct {
|
type PullRequestActivityLabel struct {
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
LabelColor enum.LabelColor `json:"label_color"`
|
LabelColor enum.LabelColor `json:"label_color"`
|
||||||
|
|
Loading…
Reference in New Issue