Compare commits

...

2 Commits

Author SHA1 Message Date
silverwind
e09813f472 docs: update AGENTS.md (#40270)
Some useful rules from my past experiences, feel free to suggest more.
Dark theme screenshots is subjective of course, but I prefer them.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-11-07 16:09:25 +00:00
Lazizbek Ergashev
884884308d fix(pull): don't add a timeline entry for pending API reviews (#40260)
Pending reviews created through the API went through `SubmitReview`,
which added a timeline entry and sent notifications before the review
was submitted, so submitting it later showed the review twice.

- Pending reviews skip the timeline entry and notifications until
submitted, like on GitHub
- `CreatePullReview` rejects comments without a `path` or a positive
line with 422, like GitHub
- Already duplicated reviews stay as they are

Fixes https://github.com/go-gitea/gitea/issues/40259

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 14:19:50 +00:00
8 changed files with 42 additions and 2 deletions

View File

@@ -1,10 +1,14 @@
- Never assume, verify before claiming
- List development targets with `make help`
- Before working on code, read the `docs` guides for every area touched: `guidelines-backend.md` for Go, `guidelines-frontend.md` for TS, CSS and Vue, both for templates, `testing.md` for tests, `development.md` for config options and migrations, `guidelines-refactoring.md` for refactors
- PR descriptions: minimal, only what and why, no task or file listings. Include screenshots for UI changes, before and after when modifying existing UI. Aim for less than 1000 characters
- Before designing a feature or behavior, check prior art in GitHub and GitLab
- Don't add code for theoretical edge cases, only for ones that can realistically occur
- Write docs concisely and user-facing, skip implementation details
- PR descriptions: minimal, only what and why, no task or file listings, no headings. Aim for less than 1000 characters. References go at the end of the description like `Fixes: <url>`.
- PR descriptions and update comments: Include screenshots for UI changes, before and after side by side. Take them at 4x scale in dark theme, cropped to the changes with some padding, and embed them at half CSS size
- Reference issues and PRs by full URL, not by number
- Use Conventional Commits for commit messages and PR titles, plus Gitea's `enhance` type for user-facing enhancements
- Add an `Assisted-by: AGENT_NAME:MODEL_VERSION` trailer to commit messages, never `Co-Authored-By` or `Signed-off-by`
- Add an `Assisted-by: AGENT_NAME:MODEL_VERSION` trailer to commit messages and PR descriptions, never `Co-Authored-By` or `Signed-off-by`
- Attribute agent authorship on one trailing line in issue and PR comments, never as a PR description section
- Never rewrite git history unless asked, update PRs with new commits and normal push
- Comments: write almost none, short and preferably same-line, explaining why for a future reader. Never narrate code, the change or the prompt. Preserve existing ones that still apply. If you need to write a paragraph-long comment, rethink your implementation, it is likely too complicated

View File

@@ -545,6 +545,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, issue *Issue, revi
}
}
if reviewType == ReviewTypePending {
return review, nil, committer.Commit()
}
comm, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReview,
Doer: doer,

View File

@@ -86,6 +86,7 @@ type CreatePullReviewOptions struct {
// CreatePullReviewComment represent a review comment for creation api
type CreatePullReviewComment struct {
// the tree path
// required: true
Path string `json:"path"`
Body string `json:"body"`
// if comment to old file line or 0

View File

@@ -487,6 +487,13 @@ func CreatePullReview(ctx *context.APIContext) {
// "$ref": "#/responses/error"
opts := web.GetForm[*api.CreatePullReviewOptions](ctx)
for _, comment := range opts.Comments {
if comment.Path == "" || (comment.NewLineNum <= 0 && comment.OldLineNum <= 0) {
ctx.APIError(http.StatusUnprocessableEntity, "review comments require a path and a positive new_position or old_position")
return
}
}
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
ctx.APIErrorAuto(err)

View File

@@ -332,6 +332,9 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
if err != nil {
return nil, nil, err
}
if reviewType == issues_model.ReviewTypePending {
return review, nil, nil
}
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, doer, comm.Content)
if err != nil {

View File

@@ -4590,6 +4590,9 @@
"x-go-name": "Path"
}
},
"required": [
"path"
],
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},

View File

@@ -27027,6 +27027,9 @@
"CreatePullReviewComment": {
"description": "CreatePullReviewComment represent a review comment for creation api",
"type": "object",
"required": [
"path"
],
"properties": {
"body": {
"type": "string",

View File

@@ -113,6 +113,20 @@ func testAPIPullReviewGeneral(t *testing.T) {
assert.EqualValues(t, comment.UpdatedUnix, reviewComments[0].Updated.Unix())
assert.Equal(t, comment.HTMLURL(t.Context()), reviewComments[0].HTMLURL)
for _, invalidComment := range []api.CreatePullReviewComment{
{Body: "missing path", NewLineNum: 1},
{Path: "README.md", Body: "missing line"},
} {
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{
Comments: []api.CreatePullReviewComment{
{Path: "README.md", Body: "comment before invalid anchor", NewLineNum: 1},
invalidComment,
},
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusUnprocessableEntity)
}
unittest.AssertNotExistsBean(t, &issues_model.Comment{IssueID: pullIssue.ID, Content: "comment before invalid anchor"})
// test CreatePullReview
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{
Body: "body1",
@@ -153,6 +167,7 @@ func testAPIPullReviewGeneral(t *testing.T) {
assert.EqualValues(t, "APPROVED", review.State)
assert.Equal(t, 3, review.CodeCommentsCount)
unittest.AssertCount(t, &issues_model.Comment{ReviewID: review.ID, Type: issues_model.CommentTypeReview}, 1)
reviewComment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ReviewID: review.ID, Type: issues_model.CommentTypeReview})
req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d", repo.OwnerName, repo.Name, reviewComment.ID), &api.EditIssueCommentOption{Body: "just one nit"}).AddTokenAuth(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue))
MakeRequest(t, req, http.StatusOK)