Skip to content

Commit 9e7866c

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* giteaofficial/main: Fix raw file API ref handling (go-gitea#33172) [skip ci] Updated translations via Crowdin Fix ACME panic (go-gitea#33178) Automerge supports deleting branch automatically after merging (go-gitea#32343) Fix branch dropdown not display ref name (go-gitea#33159) Add .run to gitignore (go-gitea#33175) Fix assignee list overlapping in Issue sidebar (go-gitea#33176) Fix pam auth test regression (go-gitea#33169) Move repo size to sidebar (go-gitea#33155) Fix fuzz test (go-gitea#33156) Refactor older tests to use testify (go-gitea#33140) [skip ci] Updated translations via Crowdin Fix typo in gitea downloader test and add missing codebase in `ToGitServiceType` (go-gitea#33146) Fix form width (go-gitea#33151) Convert github.com/xanzy/go-gitlab into gitlab.com/gitlab-org/api/client-go (go-gitea#33126) Refactor HTMLFormat, update chroma render, fix js error (go-gitea#33136) Add missed transaction on setmerged (go-gitea#33079)
2 parents b2c0630 + 65aae09 commit 9e7866c

File tree

112 files changed

+1906
-1921
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+1906
-1921
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ _test
99

1010
# IntelliJ
1111
.idea
12+
.run
1213

1314
# IntelliJ Gateway
1415
.uuid

assets/go-licenses.json

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

cmd/web_acme.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ func runACME(listenAddr string, m http.Handler) error {
5454
altTLSALPNPort = p
5555
}
5656

57-
magic := &certmagic.Default
58-
magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
57+
// FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https"
58+
// Ideally it should migrate to AppDataPath write to "AppDataPath/https"
59+
certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
60+
magic := certmagic.NewDefault()
5961
// Try to use private CA root if provided, otherwise defaults to system's trust
6062
var certPool *x509.CertPool
6163
if setting.AcmeCARoot != "" {

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ require (
2727
github.com/ProtonMail/go-crypto v1.1.4
2828
github.com/PuerkitoBio/goquery v1.10.0
2929
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
30-
github.com/alecthomas/chroma/v2 v2.14.0
30+
github.com/alecthomas/chroma/v2 v2.15.0
3131
github.com/aws/aws-sdk-go-v2/credentials v1.17.42
3232
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
3333
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@@ -112,22 +112,22 @@ require (
112112
github.com/ulikunitz/xz v0.5.12
113113
github.com/urfave/cli/v2 v2.27.5
114114
github.com/wneessen/go-mail v0.5.2
115-
github.com/xanzy/go-gitlab v0.112.0
116115
github.com/xeipuuv/gojsonschema v1.2.0
117116
github.com/yohcop/openid-go v1.0.1
118117
github.com/yuin/goldmark v1.7.8
119118
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
120119
github.com/yuin/goldmark-meta v1.1.0
120+
gitlab.com/gitlab-org/api/client-go v0.119.0
121121
golang.org/x/crypto v0.32.0
122122
golang.org/x/image v0.21.0
123123
golang.org/x/net v0.34.0
124-
golang.org/x/oauth2 v0.23.0
124+
golang.org/x/oauth2 v0.24.0
125125
golang.org/x/sync v0.10.0
126126
golang.org/x/sys v0.29.0
127127
golang.org/x/text v0.21.0
128128
golang.org/x/tools v0.29.0
129129
google.golang.org/grpc v1.67.1
130-
google.golang.org/protobuf v1.35.1
130+
google.golang.org/protobuf v1.36.0
131131
gopkg.in/ini.v1 v1.67.0
132132
gopkg.in/yaml.v3 v3.0.1
133133
mvdan.cc/xurls/v2 v2.5.0
@@ -306,7 +306,7 @@ require (
306306
go.uber.org/zap v1.27.0 // indirect
307307
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
308308
golang.org/x/mod v0.22.0 // indirect
309-
golang.org/x/time v0.7.0 // indirect
309+
golang.org/x/time v0.8.0 // indirect
310310
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
311311
gopkg.in/warnings.v0 v0.1.2 // indirect
312312
gopkg.in/yaml.v2 v2.4.0 // indirect

go.sum

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv
8181
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
8282
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 h1:BP0HiyNT3AQEYi+if3wkRcIdQFHtsw6xX3Kx0glckgA=
8383
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3/go.mod h1:hMNtySovKkn2gdDuLqnqveP+mfhUSaBdoBcr2I7Zt0E=
84-
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
85-
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
84+
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
85+
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
8686
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
87-
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
88-
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
87+
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
88+
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
8989
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
9090
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
9191
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -766,8 +766,6 @@ github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT
766766
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
767767
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
768768
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
769-
github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw=
770-
github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
771769
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
772770
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
773771
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -802,6 +800,8 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
802800
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
803801
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
804802
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
803+
gitlab.com/gitlab-org/api/client-go v0.119.0 h1:YBZyx9XUTtEDBBYtY36cZWz6JmT7om/8HPSk37IS95g=
804+
gitlab.com/gitlab-org/api/client-go v0.119.0/go.mod h1:ygHmS3AU3TpvK+AC6DYO1QuAxLlv6yxYK+/Votr/WFQ=
805805
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
806806
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
807807
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
@@ -867,8 +867,8 @@ golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
867867
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
868868
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
869869
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
870-
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
871-
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
870+
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
871+
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
872872
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
873873
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
874874
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -942,8 +942,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
942942
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
943943
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
944944
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
945-
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
946-
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
945+
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
946+
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
947947
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
948948
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
949949
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -970,8 +970,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
970970
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
971971
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
972972
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
973-
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
974-
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
973+
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
974+
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
975975
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
976976
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
977977
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

models/asymkey/gpg_key_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/keybase/go-crypto/openpgp/packet"
1717
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
1819
)
1920

2021
func TestCheckArmoredGPGKeyString(t *testing.T) {
@@ -107,9 +108,8 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
107108
=i9b7
108109
-----END PGP PUBLIC KEY BLOCK-----`
109110
keys, err := checkArmoredGPGKeyString(testGPGArmor)
110-
if !assert.NotEmpty(t, keys) {
111-
return
112-
}
111+
require.NotEmpty(t, keys)
112+
113113
ekey := keys[0]
114114
assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey)
115115

models/db/engine_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
1616

1717
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
1819
)
1920

2021
func TestDumpDatabase(t *testing.T) {
@@ -62,9 +63,7 @@ func TestPrimaryKeys(t *testing.T) {
6263
// Import "code.gitea.io/gitea/cmd" to make sure each db.RegisterModel in init functions has been called.
6364

6465
beans, err := db.NamesToBean()
65-
if err != nil {
66-
t.Fatal(err)
67-
}
66+
require.NoError(t, err)
6867

6968
whitelist := map[string]string{
7069
"the_table_name_to_skip_checking": "Write a note here to explain why",
@@ -79,8 +78,6 @@ func TestPrimaryKeys(t *testing.T) {
7978
t.Logf("ignore %q because %q", table.Name, why)
8079
continue
8180
}
82-
if len(table.PrimaryKeys) == 0 {
83-
t.Errorf("table %q has no primary key", table.Name)
84-
}
81+
assert.NotEmpty(t, table.PrimaryKeys, "table %q has no primary key", table.Name)
8582
}
8683
}

models/issues/issue.go

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,6 @@ func (err ErrIssueNotExist) Unwrap() error {
4646
return util.ErrNotExist
4747
}
4848

49-
// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
50-
type ErrIssueIsClosed struct {
51-
ID int64
52-
RepoID int64
53-
Index int64
54-
}
55-
56-
// IsErrIssueIsClosed checks if an error is a ErrIssueNotExist.
57-
func IsErrIssueIsClosed(err error) bool {
58-
_, ok := err.(ErrIssueIsClosed)
59-
return ok
60-
}
61-
62-
func (err ErrIssueIsClosed) Error() string {
63-
return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
64-
}
65-
6649
// ErrNewIssueInsert is used when the INSERT statement in newIssue fails
6750
type ErrNewIssueInsert struct {
6851
OriginalError error
@@ -78,22 +61,6 @@ func (err ErrNewIssueInsert) Error() string {
7861
return err.OriginalError.Error()
7962
}
8063

81-
// ErrIssueWasClosed is used when close a closed issue
82-
type ErrIssueWasClosed struct {
83-
ID int64
84-
Index int64
85-
}
86-
87-
// IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed.
88-
func IsErrIssueWasClosed(err error) bool {
89-
_, ok := err.(ErrIssueWasClosed)
90-
return ok
91-
}
92-
93-
func (err ErrIssueWasClosed) Error() string {
94-
return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
95-
}
96-
9764
var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed")
9865

9966
// Issue represents an issue or pull request of repository.

models/issues/issue_update.go

Lines changed: 81 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,38 +28,40 @@ import (
2828

2929
// UpdateIssueCols updates cols of issue
3030
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
31-
if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil {
32-
return err
33-
}
34-
return nil
31+
_, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue)
32+
return err
3533
}
3634

37-
func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
38-
// Reload the issue
39-
currentIssue, err := GetIssueByID(ctx, issue.ID)
40-
if err != nil {
41-
return nil, err
42-
}
35+
// ErrIssueIsClosed is used when close a closed issue
36+
type ErrIssueIsClosed struct {
37+
ID int64
38+
RepoID int64
39+
Index int64
40+
IsPull bool
41+
}
4342

44-
// Nothing should be performed if current status is same as target status
45-
if currentIssue.IsClosed == isClosed {
46-
if !issue.IsPull {
47-
return nil, ErrIssueWasClosed{
48-
ID: issue.ID,
49-
}
50-
}
51-
return nil, ErrPullWasClosed{
52-
ID: issue.ID,
53-
}
54-
}
43+
// IsErrIssueIsClosed checks if an error is a ErrIssueIsClosed.
44+
func IsErrIssueIsClosed(err error) bool {
45+
_, ok := err.(ErrIssueIsClosed)
46+
return ok
47+
}
5548

56-
issue.IsClosed = isClosed
57-
return doChangeIssueStatus(ctx, issue, doer, isMergePull)
49+
func (err ErrIssueIsClosed) Error() string {
50+
return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already closed", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
5851
}
5952

60-
func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
53+
func SetIssueAsClosed(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
54+
if issue.IsClosed {
55+
return nil, ErrIssueIsClosed{
56+
ID: issue.ID,
57+
RepoID: issue.RepoID,
58+
Index: issue.Index,
59+
IsPull: issue.IsPull,
60+
}
61+
}
62+
6163
// Check for open dependencies
62-
if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) {
64+
if issue.Repo.IsDependenciesEnabled(ctx) {
6365
// only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies
6466
noDeps, err := IssueNoDependenciesLeft(ctx, issue)
6567
if err != nil {
@@ -71,16 +73,63 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
7173
}
7274
}
7375

74-
if issue.IsClosed {
75-
issue.ClosedUnix = timeutil.TimeStampNow()
76-
} else {
77-
issue.ClosedUnix = 0
76+
issue.IsClosed = true
77+
issue.ClosedUnix = timeutil.TimeStampNow()
78+
79+
if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
80+
Where("is_closed = ?", false).
81+
Update(issue); err != nil {
82+
return nil, err
83+
} else if cnt != 1 {
84+
return nil, ErrIssueAlreadyChanged
7885
}
7986

80-
if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_unix"); err != nil {
87+
return updateIssueNumbers(ctx, issue, doer, util.Iif(isMergePull, CommentTypeMergePull, CommentTypeClose))
88+
}
89+
90+
// ErrIssueIsOpen is used when reopen an opened issue
91+
type ErrIssueIsOpen struct {
92+
ID int64
93+
RepoID int64
94+
IsPull bool
95+
Index int64
96+
}
97+
98+
// IsErrIssueIsOpen checks if an error is a ErrIssueIsOpen.
99+
func IsErrIssueIsOpen(err error) bool {
100+
_, ok := err.(ErrIssueIsOpen)
101+
return ok
102+
}
103+
104+
func (err ErrIssueIsOpen) Error() string {
105+
return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already open", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
106+
}
107+
108+
func setIssueAsReopen(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
109+
if !issue.IsClosed {
110+
return nil, ErrIssueIsOpen{
111+
ID: issue.ID,
112+
RepoID: issue.RepoID,
113+
Index: issue.Index,
114+
IsPull: issue.IsPull,
115+
}
116+
}
117+
118+
issue.IsClosed = false
119+
issue.ClosedUnix = 0
120+
121+
if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
122+
Where("is_closed = ?", true).
123+
Update(issue); err != nil {
81124
return nil, err
125+
} else if cnt != 1 {
126+
return nil, ErrIssueAlreadyChanged
82127
}
83128

129+
return updateIssueNumbers(ctx, issue, doer, CommentTypeReopen)
130+
}
131+
132+
func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User, cmtType CommentType) (*Comment, error) {
84133
// Update issue count of labels
85134
if err := issue.LoadLabels(ctx); err != nil {
86135
return nil, err
@@ -103,14 +152,6 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
103152
return nil, err
104153
}
105154

106-
// New action comment
107-
cmtType := CommentTypeClose
108-
if !issue.IsClosed {
109-
cmtType = CommentTypeReopen
110-
} else if isMergePull {
111-
cmtType = CommentTypeMergePull
112-
}
113-
114155
return CreateComment(ctx, &CreateCommentOptions{
115156
Type: cmtType,
116157
Doer: doer,
@@ -134,7 +175,7 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
134175
}
135176
defer committer.Close()
136177

137-
comment, err := ChangeIssueStatus(ctx, issue, doer, true, false)
178+
comment, err := SetIssueAsClosed(ctx, issue, doer, false)
138179
if err != nil {
139180
return nil, err
140181
}
@@ -159,7 +200,7 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
159200
}
160201
defer committer.Close()
161202

162-
comment, err := ChangeIssueStatus(ctx, issue, doer, false, false)
203+
comment, err := setIssueAsReopen(ctx, issue, doer)
163204
if err != nil {
164205
return nil, err
165206
}

0 commit comments

Comments
 (0)