Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview: improve patch validation

automatically adds a newline to patches that are missing one.

authored by

oppiliappan and committed by
Tangled
33b5a9a5 85870885

+77 -41
+18 -22
appview/pulls/pulls.go
··· 23 23 "tangled.org/core/appview/pages" 24 24 "tangled.org/core/appview/pages/markup" 25 25 "tangled.org/core/appview/reporesolver" 26 + "tangled.org/core/appview/validator" 26 27 "tangled.org/core/appview/xrpcclient" 27 28 "tangled.org/core/idresolver" 28 29 "tangled.org/core/patchutil" ··· 48 47 notifier notify.Notifier 49 48 enforcer *rbac.Enforcer 50 49 logger *slog.Logger 50 + validator *validator.Validator 51 51 } 52 52 53 53 func New( ··· 60 58 config *config.Config, 61 59 notifier notify.Notifier, 62 60 enforcer *rbac.Enforcer, 61 + validator *validator.Validator, 63 62 logger *slog.Logger, 64 63 ) *Pulls { 65 64 return &Pulls{ ··· 73 70 notifier: notifier, 74 71 enforcer: enforcer, 75 72 logger: logger, 73 + validator: validator, 76 74 } 77 75 } 78 76 ··· 969 965 patch := comparison.FormatPatchRaw 970 966 combined := comparison.CombinedPatchRaw 971 967 972 - if !patchutil.IsPatchValid(patch) { 968 + if err := s.validator.ValidatePatch(&patch); err != nil { 969 + s.logger.Error("failed to validate patch", "err", err) 973 970 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 974 971 return 975 972 } ··· 987 982 } 988 983 989 984 func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) { 990 - if !patchutil.IsPatchValid(patch) { 985 + if err := s.validator.ValidatePatch(&patch); err != nil { 986 + s.logger.Error("patch validation failed", "err", err) 991 987 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 992 988 return 993 989 } ··· 1079 1073 patch := comparison.FormatPatchRaw 1080 1074 combined := comparison.CombinedPatchRaw 1081 1075 1082 - if !patchutil.IsPatchValid(patch) { 1076 + if err := s.validator.ValidatePatch(&patch); err != nil { 1077 + s.logger.Error("failed to validate patch", "err", err) 1083 1078 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 1084 1079 return 1085 1080 } ··· 1344 1337 return 1345 1338 } 1346 1339 1347 - if patch == "" || !patchutil.IsPatchValid(patch) { 1340 + if err := s.validator.ValidatePatch(&patch); err != nil { 1341 + s.logger.Error("faield to validate patch", "err", err) 1348 1342 s.pages.Notice(w, "patch-error", "Invalid patch format. Please provide a valid git diff or format-patch.") 1349 1343 return 1350 1344 } ··· 1762 1754 s.resubmitPullHelper(w, r, f, user, pull, patch, combined, sourceRev) 1763 1755 } 1764 1756 1765 - // validate a resubmission against a pull request 1766 - func validateResubmittedPatch(pull *models.Pull, patch string) error { 1767 - if patch == "" { 1768 - return fmt.Errorf("Patch is empty.") 1769 - } 1770 - 1771 - if patch == pull.LatestPatch() { 1772 - return fmt.Errorf("Patch is identical to previous submission.") 1773 - } 1774 - 1775 - if !patchutil.IsPatchValid(patch) { 1776 - return fmt.Errorf("Invalid patch format. Please provide a valid diff.") 1777 - } 1778 - 1779 - return nil 1780 - } 1781 - 1782 1757 func (s *Pulls) resubmitPullHelper( 1783 1758 w http.ResponseWriter, 1784 1759 r *http.Request, ··· 1778 1787 return 1779 1788 } 1780 1789 1781 - if err := validateResubmittedPatch(pull, patch); err != nil { 1790 + if err := s.validator.ValidatePatch(&patch); err != nil { 1782 1791 s.pages.Notice(w, "resubmit-error", err.Error()) 1792 + return 1793 + } 1794 + 1795 + if patch == pull.LatestPatch() { 1796 + s.pages.Notice(w, "resubmit-error", "Patch is identical to previous submission.") 1783 1797 return 1784 1798 } 1785 1799
+1
appview/state/router.go
··· 277 277 s.config, 278 278 s.notifier, 279 279 s.enforcer, 280 + s.validator, 280 281 log.SubLogger(s.logger, "pulls"), 281 282 ) 282 283 return pulls.Router(mw)
+25
appview/validator/patch.go
··· 1 + package validator 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + 7 + "tangled.org/core/patchutil" 8 + ) 9 + 10 + func (v *Validator) ValidatePatch(patch *string) error { 11 + if patch == nil || *patch == "" { 12 + return fmt.Errorf("patch is empty") 13 + } 14 + 15 + // add newline if not present to diff style patches 16 + if !patchutil.IsFormatPatch(*patch) && !strings.HasSuffix(*patch, "\n") { 17 + *patch = *patch + "\n" 18 + } 19 + 20 + if err := patchutil.IsPatchValid(*patch); err != nil { 21 + return err 22 + } 23 + 24 + return nil 25 + }
+2
knotserver/xrpc/merge_check.go
··· 81 81 } 82 82 } 83 83 84 + l.Debug("merge check response", "isConflicted", response.Is_conflicted, "err", response.Error, "conflicts", response.Conflicts) 85 + 84 86 w.Header().Set("Content-Type", "application/json") 85 87 w.WriteHeader(http.StatusOK) 86 88 json.NewEncoder(w).Encode(response)
+18 -7
patchutil/patchutil.go
··· 1 1 package patchutil 2 2 3 3 import ( 4 + "errors" 4 5 "fmt" 5 6 "log" 6 7 "os" ··· 43 42 // IsPatchValid checks if the given patch string is valid. 44 43 // It performs very basic sniffing for either git-diff or git-format-patch 45 44 // header lines. For format patches, it attempts to extract and validate each one. 46 - func IsPatchValid(patch string) bool { 45 + var ( 46 + EmptyPatchError error = errors.New("patch is empty") 47 + GenericPatchError error = errors.New("patch is invalid") 48 + FormatPatchError error = errors.New("patch is not a valid format-patch") 49 + ) 50 + 51 + func IsPatchValid(patch string) error { 47 52 if len(patch) == 0 { 48 - return false 53 + return EmptyPatchError 49 54 } 50 55 51 56 lines := strings.Split(patch, "\n") 52 57 if len(lines) < 2 { 53 - return false 58 + return EmptyPatchError 54 59 } 55 60 56 61 firstLine := strings.TrimSpace(lines[0]) ··· 67 60 strings.HasPrefix(firstLine, "Index: ") || 68 61 strings.HasPrefix(firstLine, "+++ ") || 69 62 strings.HasPrefix(firstLine, "@@ ") { 70 - return true 63 + return nil 71 64 } 72 65 73 66 // check if it's format-patch ··· 77 70 // it's safe to say it's broken. 78 71 patches, err := ExtractPatches(patch) 79 72 if err != nil { 80 - return false 73 + return fmt.Errorf("%w: %w", FormatPatchError, err) 81 74 } 82 - return len(patches) > 0 75 + if len(patches) == 0 { 76 + return EmptyPatchError 77 + } 78 + 79 + return nil 83 80 } 84 81 85 - return false 82 + return GenericPatchError 86 83 } 87 84 88 85 func IsFormatPatch(patch string) bool {
+13 -12
patchutil/patchutil_test.go
··· 1 1 package patchutil 2 2 3 3 import ( 4 + "errors" 4 5 "reflect" 5 6 "testing" 6 7 ) ··· 10 9 tests := []struct { 11 10 name string 12 11 patch string 13 - expected bool 12 + expected error 14 13 }{ 15 14 { 16 15 name: `empty patch`, 17 16 patch: ``, 18 - expected: false, 17 + expected: EmptyPatchError, 19 18 }, 20 19 { 21 20 name: `single line patch`, 22 21 patch: `single line`, 23 - expected: false, 22 + expected: EmptyPatchError, 24 23 }, 25 24 { 26 25 name: `valid diff patch`, ··· 32 31 -old line 33 32 +new line 34 33 context`, 35 - expected: true, 34 + expected: nil, 36 35 }, 37 36 { 38 37 name: `valid patch starting with ---`, ··· 42 41 -old line 43 42 +new line 44 43 context`, 45 - expected: true, 44 + expected: nil, 46 45 }, 47 46 { 48 47 name: `valid patch starting with Index`, ··· 54 53 -old line 55 54 +new line 56 55 context`, 57 - expected: true, 56 + expected: nil, 58 57 }, 59 58 { 60 59 name: `valid patch starting with +++`, ··· 64 63 -old line 65 64 +new line 66 65 context`, 67 - expected: true, 66 + expected: nil, 68 67 }, 69 68 { 70 69 name: `valid patch starting with @@`, ··· 73 72 +new line 74 73 context 75 74 `, 76 - expected: true, 75 + expected: nil, 77 76 }, 78 77 { 79 78 name: `valid format patch`, ··· 91 90 +new content 92 91 -- 93 92 2.48.1`, 94 - expected: true, 93 + expected: nil, 95 94 }, 96 95 { 97 96 name: `invalid format patch`, 98 97 patch: `From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001 99 98 From: Author <author@example.com> 100 99 This is not a valid patch format`, 101 - expected: false, 100 + expected: FormatPatchError, 102 101 }, 103 102 { 104 103 name: `not a patch at all`, ··· 106 105 just some 107 106 random text 108 107 that isn't a patch`, 109 - expected: false, 108 + expected: GenericPatchError, 110 109 }, 111 110 } 112 111 113 112 for _, tt := range tests { 114 113 t.Run(tt.name, func(t *testing.T) { 115 114 result := IsPatchValid(tt.patch) 116 - if result != tt.expected { 115 + if !errors.Is(result, tt.expected) { 117 116 t.Errorf("IsPatchValid() = %v, want %v", result, tt.expected) 118 117 } 119 118 })