Skip to content

Commit 2ffa0e2

Browse files
authored
Merge branch 'main' into webhook-bark
2 parents 4786ee1 + 9a73a1f commit 2ffa0e2

File tree

11 files changed

+169
-79
lines changed

11 files changed

+169
-79
lines changed

modules/structs/repo_file.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,6 @@ type FileOptions struct {
2424
Signoff bool `json:"signoff"`
2525
}
2626

27-
type FileOptionsWithSHA struct {
28-
FileOptions
29-
// the blob ID (SHA) for the file that already exists, it is required for changing existing files
30-
// required: true
31-
SHA string `json:"sha" binding:"Required"`
32-
}
33-
3427
func (f *FileOptions) GetFileOptions() *FileOptions {
3528
return f
3629
}
@@ -41,7 +34,7 @@ type FileOptionsInterface interface {
4134

4235
var _ FileOptionsInterface = (*FileOptions)(nil)
4336

44-
// CreateFileOptions options for creating files
37+
// CreateFileOptions options for creating a file
4538
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
4639
type CreateFileOptions struct {
4740
FileOptions
@@ -50,16 +43,21 @@ type CreateFileOptions struct {
5043
ContentBase64 string `json:"content"`
5144
}
5245

53-
// DeleteFileOptions options for deleting files (used for other File structs below)
46+
// DeleteFileOptions options for deleting a file
5447
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
5548
type DeleteFileOptions struct {
56-
FileOptionsWithSHA
49+
FileOptions
50+
// the blob ID (SHA) for the file to delete
51+
// required: true
52+
SHA string `json:"sha" binding:"Required"`
5753
}
5854

59-
// UpdateFileOptions options for updating files
55+
// UpdateFileOptions options for updating or creating a file
6056
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
6157
type UpdateFileOptions struct {
62-
FileOptionsWithSHA
58+
FileOptions
59+
// the blob ID (SHA) for the file that already exists to update, or leave it empty to create a new file
60+
SHA string `json:"sha"`
6361
// content must be base64 encoded
6462
// required: true
6563
ContentBase64 string `json:"content"`

routers/api/v1/repo/file.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ func CreateFile(ctx *context.APIContext) {
525525
func UpdateFile(ctx *context.APIContext) {
526526
// swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoUpdateFile
527527
// ---
528-
// summary: Update a file in a repository
528+
// summary: Update a file in a repository if SHA is set, or create the file if SHA is not set
529529
// consumes:
530530
// - application/json
531531
// produces:
@@ -554,6 +554,8 @@ func UpdateFile(ctx *context.APIContext) {
554554
// responses:
555555
// "200":
556556
// "$ref": "#/responses/FileResponse"
557+
// "201":
558+
// "$ref": "#/responses/FileResponse"
557559
// "403":
558560
// "$ref": "#/responses/error"
559561
// "404":
@@ -572,8 +574,9 @@ func UpdateFile(ctx *context.APIContext) {
572574
ctx.APIError(http.StatusUnprocessableEntity, err)
573575
return
574576
}
577+
willCreate := apiOpts.SHA == ""
575578
opts.Files = append(opts.Files, &files_service.ChangeRepoFile{
576-
Operation: "update",
579+
Operation: util.Iif(willCreate, "create", "update"),
577580
ContentReader: contentReader,
578581
SHA: apiOpts.SHA,
579582
FromTreePath: apiOpts.FromPath,
@@ -587,7 +590,7 @@ func UpdateFile(ctx *context.APIContext) {
587590
handleChangeRepoFilesError(ctx, err)
588591
} else {
589592
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
590-
ctx.JSON(http.StatusOK, fileResponse)
593+
ctx.JSON(util.Iif(willCreate, http.StatusCreated, http.StatusOK), fileResponse)
591594
}
592595
}
593596

services/pull/comment.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *iss
6969
if err != nil {
7070
return nil, err
7171
}
72+
// It maybe an empty pull request. Only non-empty pull request need to create push comment
73+
if len(data.CommitIDs) == 0 {
74+
return nil, nil
75+
}
7276
}
7377

7478
dataJSON, err := json.Marshal(data)

services/pull/pull.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
155155

156156
issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
157157

158+
// Request reviews, these should be requested before other notifications because they will add request reviews record
159+
// on database
160+
permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster)
161+
for _, reviewer := range opts.Reviewers {
162+
if _, err = issue_service.ReviewRequest(ctx, pr.Issue, issue.Poster, &permDoer, reviewer, true); err != nil {
163+
return err
164+
}
165+
}
166+
for _, teamReviewer := range opts.TeamReviewers {
167+
if _, err = issue_service.TeamReviewRequest(ctx, pr.Issue, issue.Poster, teamReviewer, true); err != nil {
168+
return err
169+
}
170+
}
171+
158172
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content)
159173
if err != nil {
160174
return err
@@ -173,17 +187,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
173187
}
174188
notify_service.IssueChangeAssignee(ctx, issue.Poster, issue, assignee, false, assigneeCommentMap[assigneeID])
175189
}
176-
permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster)
177-
for _, reviewer := range opts.Reviewers {
178-
if _, err = issue_service.ReviewRequest(ctx, pr.Issue, issue.Poster, &permDoer, reviewer, true); err != nil {
179-
return err
180-
}
181-
}
182-
for _, teamReviewer := range opts.TeamReviewers {
183-
if _, err = issue_service.TeamReviewRequest(ctx, pr.Issue, issue.Poster, teamReviewer, true); err != nil {
184-
return err
185-
}
186-
}
190+
187191
return nil
188192
}
189193

templates/swagger/v1_json.tmpl

Lines changed: 9 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/integration/api_repo_file_delete_test.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,19 @@ import (
2020

2121
func getDeleteFileOptions() *api.DeleteFileOptions {
2222
return &api.DeleteFileOptions{
23-
FileOptionsWithSHA: api.FileOptionsWithSHA{
24-
FileOptions: api.FileOptions{
25-
BranchName: "master",
26-
NewBranchName: "master",
27-
Message: "Removing the file new/file.txt",
28-
Author: api.Identity{
29-
Name: "John Doe",
30-
31-
},
32-
Committer: api.Identity{
33-
Name: "Jane Doe",
34-
35-
},
23+
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
24+
FileOptions: api.FileOptions{
25+
BranchName: "master",
26+
NewBranchName: "master",
27+
Message: "Removing the file new/file.txt",
28+
Author: api.Identity{
29+
Name: "John Doe",
30+
31+
},
32+
Committer: api.Identity{
33+
Name: "Jane Doe",
34+
3635
},
37-
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
3836
},
3937
}
4038
}

tests/integration/api_repo_file_update_test.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,19 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
2828
content := "This is updated text"
2929
contentEncoded := base64.StdEncoding.EncodeToString([]byte(content))
3030
return &api.UpdateFileOptions{
31-
FileOptionsWithSHA: api.FileOptionsWithSHA{
32-
FileOptions: api.FileOptions{
33-
BranchName: "master",
34-
NewBranchName: "master",
35-
Message: "My update of new/file.txt",
36-
Author: api.Identity{
37-
Name: "John Doe",
38-
39-
},
40-
Committer: api.Identity{
41-
Name: "Anne Doe",
42-
43-
},
31+
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
32+
FileOptions: api.FileOptions{
33+
BranchName: "master",
34+
NewBranchName: "master",
35+
Message: "My update of new/file.txt",
36+
Author: api.Identity{
37+
Name: "John Doe",
38+
39+
},
40+
Committer: api.Identity{
41+
Name: "Anne Doe",
42+
4443
},
45-
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
4644
},
4745
ContentBase64: contentEncoded,
4846
}
@@ -180,6 +178,15 @@ func TestAPIUpdateFile(t *testing.T) {
180178
assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
181179
assert.Equal(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message)
182180

181+
// Test updating a file without SHA (should create the file)
182+
updateFileOptions = getUpdateFileOptions()
183+
updateFileOptions.SHA = ""
184+
req = NewRequestWithJSON(t, "PUT", "/api/v1/repos/user2/repo1/contents/update-create.txt", &updateFileOptions).AddTokenAuth(token2)
185+
resp = MakeRequest(t, req, http.StatusCreated)
186+
DecodeJSON(t, resp, &fileResponse)
187+
assert.Equal(t, "08bd14b2e2852529157324de9c226b3364e76136", fileResponse.Content.SHA)
188+
assert.Equal(t, setting.AppURL+"user2/repo1/raw/branch/master/update-create.txt", *fileResponse.Content.DownloadURL)
189+
183190
// Test updating a file and renaming it
184191
updateFileOptions = getUpdateFileOptions()
185192
updateFileOptions.BranchName = repo1.DefaultBranch

tests/integration/pull_compare_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,15 @@ func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
102102

103103
// user4 creates a new branch and a PR
104104
testEditFileToNewBranch(t, user4Session, "user4", forkedRepoName, "master", "user4/update-readme", "README.md", "Hello, World\n(Edited by user4)\n")
105-
resp := testPullCreateDirectly(t, user4Session, repo3.OwnerName, repo3.Name, "master", "user4", forkedRepoName, "user4/update-readme", "PR for user4 forked repo3")
105+
resp := testPullCreateDirectly(t, user4Session, createPullRequestOptions{
106+
BaseRepoOwner: repo3.OwnerName,
107+
BaseRepoName: repo3.Name,
108+
BaseBranch: "master",
109+
HeadRepoOwner: "user4",
110+
HeadRepoName: forkedRepoName,
111+
HeadBranch: "user4/update-readme",
112+
Title: "PR for user4 forked repo3",
113+
})
106114
prURL := test.RedirectURL(resp)
107115

108116
// user2 (admin of repo3) goes to the PR files page

tests/integration/pull_create_test.go

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,50 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSel
6060
return resp
6161
}
6262

63-
func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder {
64-
headCompare := headBranch
65-
if headRepoOwner != "" {
66-
if headRepoName != "" {
67-
headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch)
63+
type createPullRequestOptions struct {
64+
BaseRepoOwner string
65+
BaseRepoName string
66+
BaseBranch string
67+
HeadRepoOwner string
68+
HeadRepoName string
69+
HeadBranch string
70+
Title string
71+
ReviewerIDs string // comma-separated list of user IDs
72+
}
73+
74+
func (opts createPullRequestOptions) IsValid() bool {
75+
return opts.BaseRepoOwner != "" && opts.BaseRepoName != "" && opts.BaseBranch != "" &&
76+
opts.HeadBranch != "" && opts.Title != ""
77+
}
78+
79+
func testPullCreateDirectly(t *testing.T, session *TestSession, opts createPullRequestOptions) *httptest.ResponseRecorder {
80+
if !opts.IsValid() {
81+
t.Fatal("Invalid pull request options")
82+
}
83+
84+
headCompare := opts.HeadBranch
85+
if opts.HeadRepoOwner != "" {
86+
if opts.HeadRepoName != "" {
87+
headCompare = fmt.Sprintf("%s/%s:%s", opts.HeadRepoOwner, opts.HeadRepoName, opts.HeadBranch)
6888
} else {
69-
headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch)
89+
headCompare = fmt.Sprintf("%s:%s", opts.HeadRepoOwner, opts.HeadBranch)
7090
}
7191
}
72-
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare))
92+
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", opts.BaseRepoOwner, opts.BaseRepoName, opts.BaseBranch, headCompare))
7393
resp := session.MakeRequest(t, req, http.StatusOK)
7494

7595
// Submit the form for creating the pull
7696
htmlDoc := NewHTMLParser(t, resp.Body)
7797
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
7898
assert.True(t, exists, "The template has changed")
79-
req = NewRequestWithValues(t, "POST", link, map[string]string{
99+
params := map[string]string{
80100
"_csrf": htmlDoc.GetCSRF(),
81-
"title": title,
82-
})
101+
"title": opts.Title,
102+
}
103+
if opts.ReviewerIDs != "" {
104+
params["reviewer_ids"] = opts.ReviewerIDs
105+
}
106+
req = NewRequestWithValues(t, "POST", link, params)
83107
resp = session.MakeRequest(t, req, http.StatusOK)
84108
return resp
85109
}
@@ -246,7 +270,15 @@ func TestPullCreatePrFromBaseToFork(t *testing.T) {
246270
testEditFile(t, sessionBase, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
247271

248272
// Create a PR
249-
resp := testPullCreateDirectly(t, sessionFork, "user1", "repo1", "master", "user2", "repo1", "master", "This is a pull title")
273+
resp := testPullCreateDirectly(t, sessionFork, createPullRequestOptions{
274+
BaseRepoOwner: "user1",
275+
BaseRepoName: "repo1",
276+
BaseBranch: "master",
277+
HeadRepoOwner: "user2",
278+
HeadRepoName: "repo1",
279+
HeadBranch: "master",
280+
Title: "This is a pull title",
281+
})
250282
// check the redirected URL
251283
url := test.RedirectURL(resp)
252284
assert.Regexp(t, "^/user1/repo1/pulls/[0-9]*$", url)

0 commit comments

Comments
 (0)