summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorJimmy Praet <jimmy.praet@telenet.be>2021-09-11 16:21:17 +0200
committerGitHub <noreply@github.com>2021-09-11 16:21:17 +0200
commit3d6cb25e315c0d4249c5c749a2eb8c64ec463aad (patch)
treebf9ba30a34e0e97b6a52584caffa1d3d9fe2506d /modules
parenteb03e819d323f6374d0a99a5b80d4674a18fa957 (diff)
downloadgitea-3d6cb25e315c0d4249c5c749a2eb8c64ec463aad.tar.gz
gitea-3d6cb25e315c0d4249c5c749a2eb8c64ec463aad.zip
Support unprotected file patterns (#16395)
Fixes #16381 Note that changes to unprotected files via the web editor still cannot be pushed directly to the protected branch. I could easily add such support for edits and deletes if needed. But for adding, uploading or renaming unprotected files, it is not trivial. * Extract & Move GetAffectedFiles to modules/git
Diffstat (limited to 'modules')
-rw-r--r--modules/convert/convert.go1
-rw-r--r--modules/git/diff.go44
-rw-r--r--modules/repofiles/delete.go33
-rw-r--r--modules/repofiles/update.go73
-rw-r--r--modules/structs/repo_branch.go3
5 files changed, 92 insertions, 62 deletions
diff --git a/modules/convert/convert.go b/modules/convert/convert.go
index 9a4714b4e0..404786ec9c 100644
--- a/modules/convert/convert.go
+++ b/modules/convert/convert.go
@@ -127,6 +127,7 @@ func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
DismissStaleApprovals: bp.DismissStaleApprovals,
RequireSignedCommits: bp.RequireSignedCommits,
ProtectedFilePatterns: bp.ProtectedFilePatterns,
+ UnprotectedFilePatterns: bp.UnprotectedFilePatterns,
Created: bp.CreatedUnix.AsTime(),
Updated: bp.UpdatedUnix.AsTime(),
}
diff --git a/modules/git/diff.go b/modules/git/diff.go
index 20f25c1bee..b473dc73f6 100644
--- a/modules/git/diff.go
+++ b/modules/git/diff.go
@@ -10,6 +10,7 @@ import (
"context"
"fmt"
"io"
+ "os"
"os/exec"
"regexp"
"strconv"
@@ -273,3 +274,46 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
oldBegin, oldNumOfLines, newBegin, newNumOfLines)
return strings.Join(newHunk, "\n"), nil
}
+
+// GetAffectedFiles returns the affected files between two commits
+func GetAffectedFiles(oldCommitID, newCommitID string, env []string, repo *Repository) ([]string, error) {
+ stdoutReader, stdoutWriter, err := os.Pipe()
+ if err != nil {
+ log.Error("Unable to create os.Pipe for %s", repo.Path)
+ return nil, err
+ }
+ defer func() {
+ _ = stdoutReader.Close()
+ _ = stdoutWriter.Close()
+ }()
+
+ affectedFiles := make([]string, 0, 32)
+
+ // Run `git diff --name-only` to get the names of the changed files
+ err = NewCommand("diff", "--name-only", oldCommitID, newCommitID).
+ RunInDirTimeoutEnvFullPipelineFunc(env, -1, repo.Path,
+ stdoutWriter, nil, nil,
+ func(ctx context.Context, cancel context.CancelFunc) error {
+ // Close the writer end of the pipe to begin processing
+ _ = stdoutWriter.Close()
+ defer func() {
+ // Close the reader on return to terminate the git command if necessary
+ _ = stdoutReader.Close()
+ }()
+ // Now scan the output from the command
+ scanner := bufio.NewScanner(stdoutReader)
+ for scanner.Scan() {
+ path := strings.TrimSpace(scanner.Text())
+ if len(path) == 0 {
+ continue
+ }
+ affectedFiles = append(affectedFiles, path)
+ }
+ return scanner.Err()
+ })
+ if err != nil {
+ log.Error("Unable to get affected files for commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
+ }
+
+ return affectedFiles, err
+}
diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go
index 2b8ddf3cc2..5ae418b7f6 100644
--- a/modules/repofiles/delete.go
+++ b/modules/repofiles/delete.go
@@ -56,37 +56,8 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo
BranchName: opts.NewBranch,
}
}
- } else {
- protectedBranch, err := repo.GetBranchProtection(opts.OldBranch)
- if err != nil {
- return nil, err
- }
- if protectedBranch != nil {
- if !protectedBranch.CanUserPush(doer.ID) {
- return nil, models.ErrUserCannotCommit{
- UserName: doer.LowerName,
- }
- }
- if protectedBranch.RequireSignedCommits {
- _, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch)
- if err != nil {
- if !models.IsErrWontSign(err) {
- return nil, err
- }
- return nil, models.ErrUserCannotCommit{
- UserName: doer.LowerName,
- }
- }
- }
- patterns := protectedBranch.GetProtectedFilePatterns()
- for _, pat := range patterns {
- if pat.Match(strings.ToLower(opts.TreePath)) {
- return nil, models.ErrFilePathProtected{
- Path: opts.TreePath,
- }
- }
- }
- }
+ } else if err := VerifyBranchProtection(repo, doer, opts.OldBranch, opts.TreePath); err != nil {
+ return nil, err
}
// Check that the path given in opts.treeName is valid (not a git path)
diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go
index ad984c465a..dc2893cb1c 100644
--- a/modules/repofiles/update.go
+++ b/modules/repofiles/update.go
@@ -148,37 +148,8 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
if err != nil && !git.IsErrBranchNotExist(err) {
return nil, err
}
- } else {
- protectedBranch, err := repo.GetBranchProtection(opts.OldBranch)
- if err != nil {
- return nil, err
- }
- if protectedBranch != nil {
- if !protectedBranch.CanUserPush(doer.ID) {
- return nil, models.ErrUserCannotCommit{
- UserName: doer.LowerName,
- }
- }
- if protectedBranch.RequireSignedCommits {
- _, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch)
- if err != nil {
- if !models.IsErrWontSign(err) {
- return nil, err
- }
- return nil, models.ErrUserCannotCommit{
- UserName: doer.LowerName,
- }
- }
- }
- patterns := protectedBranch.GetProtectedFilePatterns()
- for _, pat := range patterns {
- if pat.Match(strings.ToLower(opts.TreePath)) {
- return nil, models.ErrFilePathProtected{
- Path: opts.TreePath,
- }
- }
- }
- }
+ } else if err := VerifyBranchProtection(repo, doer, opts.OldBranch, opts.TreePath); err != nil {
+ return nil, err
}
// If FromTreePath is not set, set it to the opts.TreePath
@@ -465,3 +436,43 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
}
return file, nil
}
+
+// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
+func VerifyBranchProtection(repo *models.Repository, doer *models.User, branchName string, treePath string) error {
+ protectedBranch, err := repo.GetBranchProtection(branchName)
+ if err != nil {
+ return err
+ }
+ if protectedBranch != nil {
+ isUnprotectedFile := false
+ glob := protectedBranch.GetUnprotectedFilePatterns()
+ if len(glob) != 0 {
+ isUnprotectedFile = protectedBranch.IsUnprotectedFile(glob, treePath)
+ }
+ if !protectedBranch.CanUserPush(doer.ID) && !isUnprotectedFile {
+ return models.ErrUserCannotCommit{
+ UserName: doer.LowerName,
+ }
+ }
+ if protectedBranch.RequireSignedCommits {
+ _, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), branchName)
+ if err != nil {
+ if !models.IsErrWontSign(err) {
+ return err
+ }
+ return models.ErrUserCannotCommit{
+ UserName: doer.LowerName,
+ }
+ }
+ }
+ patterns := protectedBranch.GetProtectedFilePatterns()
+ for _, pat := range patterns {
+ if pat.Match(strings.ToLower(treePath)) {
+ return models.ErrFilePathProtected{
+ Path: treePath,
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/modules/structs/repo_branch.go b/modules/structs/repo_branch.go
index efb4caecb0..1f3bc04e86 100644
--- a/modules/structs/repo_branch.go
+++ b/modules/structs/repo_branch.go
@@ -44,6 +44,7 @@ type BranchProtection struct {
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
RequireSignedCommits bool `json:"require_signed_commits"`
ProtectedFilePatterns string `json:"protected_file_patterns"`
+ UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
// swagger:strfmt date-time
@@ -73,6 +74,7 @@ type CreateBranchProtectionOption struct {
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
RequireSignedCommits bool `json:"require_signed_commits"`
ProtectedFilePatterns string `json:"protected_file_patterns"`
+ UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
}
// EditBranchProtectionOption options for editing a branch protection
@@ -97,4 +99,5 @@ type EditBranchProtectionOption struct {
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
RequireSignedCommits *bool `json:"require_signed_commits"`
ProtectedFilePatterns *string `json:"protected_file_patterns"`
+ UnprotectedFilePatterns *string `json:"unprotected_file_patterns"`
}