From 3d6cb25e315c0d4249c5c749a2eb8c64ec463aad Mon Sep 17 00:00:00 2001 From: Jimmy Praet Date: Sat, 11 Sep 2021 16:21:17 +0200 Subject: 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 --- services/forms/repo_form.go | 1 + services/pull/patch.go | 95 ++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 53 deletions(-) (limited to 'services') diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index c1c146f234..1210d5dfc5 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -199,6 +199,7 @@ type ProtectBranchForm struct { DismissStaleApprovals bool RequireSignedCommits bool ProtectedFilePatterns string + UnprotectedFilePatterns string } // Validate validates the fields diff --git a/services/pull/patch.go b/services/pull/patch.go index 72b459bf2c..73b979f647 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -245,70 +245,59 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath // CheckFileProtection check file Protection func CheckFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string, repo *git.Repository) ([]string, error) { - // 1. If there are no patterns short-circuit and just return nil if len(patterns) == 0 { return nil, nil } - - // 2. Prep the pipe - stdoutReader, stdoutWriter, err := os.Pipe() + affectedFiles, err := git.GetAffectedFiles(oldCommitID, newCommitID, env, repo) if err != nil { - log.Error("Unable to create os.Pipe for %s", repo.Path) return nil, err } - defer func() { - _ = stdoutReader.Close() - _ = stdoutWriter.Close() - }() - changedProtectedFiles := make([]string, 0, limit) - - // 3. Run `git diff --name-only` to get the names of the changed files - err = git.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 - } - lpath := strings.ToLower(path) - for _, pat := range patterns { - if pat.Match(lpath) { - changedProtectedFiles = append(changedProtectedFiles, path) - break - } - } - if len(changedProtectedFiles) >= limit { - break - } - } - - if len(changedProtectedFiles) > 0 { - return models.ErrFilePathProtected{ - Path: changedProtectedFiles[0], - } - } - return scanner.Err() - }) - // 4. log real errors if there are any... - if err != nil && !models.IsErrFilePathProtected(err) { - log.Error("Unable to check file protection for commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) + for _, affectedFile := range affectedFiles { + lpath := strings.ToLower(affectedFile) + for _, pat := range patterns { + if pat.Match(lpath) { + changedProtectedFiles = append(changedProtectedFiles, lpath) + break + } + } + if len(changedProtectedFiles) >= limit { + break + } + } + if len(changedProtectedFiles) > 0 { + err = models.ErrFilePathProtected{ + Path: changedProtectedFiles[0], + } } - return changedProtectedFiles, err } +// CheckUnprotectedFiles check if the commit only touches unprotected files +func CheckUnprotectedFiles(oldCommitID, newCommitID string, patterns []glob.Glob, env []string, repo *git.Repository) (bool, error) { + if len(patterns) == 0 { + return false, nil + } + affectedFiles, err := git.GetAffectedFiles(oldCommitID, newCommitID, env, repo) + if err != nil { + return false, err + } + for _, affectedFile := range affectedFiles { + lpath := strings.ToLower(affectedFile) + unprotected := false + for _, pat := range patterns { + if pat.Match(lpath) { + unprotected = true + break + } + } + if !unprotected { + return false, nil + } + } + return true, nil +} + // checkPullFilesProtection check if pr changed protected files and save results func checkPullFilesProtection(pr *models.PullRequest, gitRepo *git.Repository) error { if err := pr.LoadProtectedBranch(); err != nil { -- cgit v1.2.3