Skip to content

Commit f57e9d0

Browse files
authored
Merge branch 'main' into update-i.8713187.xyz/google/go-github
2 parents c8148d9 + 7ee2c13 commit f57e9d0

40 files changed

+471
-298
lines changed

.gitpod.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,6 @@ tasks:
2323
gp sync-await setup
2424
make watch-frontend
2525
openMode: split-right
26-
- name: Run docs
27-
command: |
28-
gp sync-await setup
29-
cd docs
30-
make clean update
31-
hugo server -D -F --baseUrl $(gp url 1313) --liveReloadPort=443 --appendPort=false --bind=0.0.0.0
32-
openMode: split-right
3326

3427
vscode:
3528
extensions:
@@ -46,5 +39,3 @@ vscode:
4639
ports:
4740
- name: Gitea
4841
port: 3000
49-
- name: Docs
50-
port: 1313

models/organization/org.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ func (org *Organization) IsOwnedBy(uid int64) (bool, error) {
100100
return IsOrganizationOwner(db.DefaultContext, org.ID, uid)
101101
}
102102

103+
// IsOrgAdmin returns true if given user is in the owner team or an admin team.
104+
func (org *Organization) IsOrgAdmin(uid int64) (bool, error) {
105+
return IsOrganizationAdmin(db.DefaultContext, org.ID, uid)
106+
}
107+
103108
// IsOrgMember returns true if given user is member of organization.
104109
func (org *Organization) IsOrgMember(uid int64) (bool, error) {
105110
return IsOrganizationMember(db.DefaultContext, org.ID, uid)

models/organization/org_user.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99

1010
"code.gitea.io/gitea/models/db"
11+
"code.gitea.io/gitea/models/perm"
1112
user_model "code.gitea.io/gitea/models/user"
1213
"code.gitea.io/gitea/modules/log"
1314

@@ -53,6 +54,20 @@ func IsOrganizationOwner(ctx context.Context, orgID, uid int64) (bool, error) {
5354
return IsTeamMember(ctx, orgID, ownerTeam.ID, uid)
5455
}
5556

57+
// IsOrganizationAdmin returns true if given user is in the owner team or an admin team.
58+
func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) {
59+
teams, err := GetUserOrgTeams(ctx, orgID, uid)
60+
if err != nil {
61+
return false, err
62+
}
63+
for _, t := range teams {
64+
if t.AccessMode >= perm.AccessModeAdmin {
65+
return true, nil
66+
}
67+
}
68+
return false, nil
69+
}
70+
5671
// IsOrganizationMember returns true if given user is member of organization.
5772
func IsOrganizationMember(ctx context.Context, orgID, uid int64) (bool, error) {
5873
return db.GetEngine(ctx).

modules/repository/delete.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, er
2121
}
2222

2323
if repo.Owner.IsOrganization() {
24-
isOwner, err := organization.OrgFromUser(repo.Owner).IsOwnedBy(user.ID)
24+
isAdmin, err := organization.OrgFromUser(repo.Owner).IsOrgAdmin(user.ID)
2525
if err != nil {
2626
return false, err
2727
}
28-
return isOwner, nil
28+
return isAdmin, nil
2929
}
3030

3131
return false, nil

modules/session/redis.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,21 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
183183
}
184184
}
185185

186-
if err = p.c.Rename(graceful.GetManager().HammerContext(), poldsid, psid).Err(); err != nil {
186+
// do not use Rename here, because the old sid and new sid may be in different redis cluster slot.
187+
kvs, err := p.c.Get(graceful.GetManager().HammerContext(), poldsid).Result()
188+
if err != nil {
187189
return nil, err
188190
}
189191

190-
var kv map[interface{}]interface{}
191-
kvs, err := p.c.Get(graceful.GetManager().HammerContext(), psid).Result()
192-
if err != nil {
192+
if err = p.c.Del(graceful.GetManager().HammerContext(), poldsid).Err(); err != nil {
193193
return nil, err
194194
}
195195

196+
if err = p.c.Set(graceful.GetManager().HammerContext(), psid, kvs, p.duration).Err(); err != nil {
197+
return nil, err
198+
}
199+
200+
var kv map[interface{}]interface{}
196201
if len(kvs) == 0 {
197202
kv = make(map[interface{}]interface{})
198203
} else {

modules/templates/helper.go

Lines changed: 5 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"bytes"
99
"context"
1010
"encoding/hex"
11-
"errors"
1211
"fmt"
1312
"html"
1413
"html/template"
@@ -219,20 +218,6 @@ func NewFuncMap() []template.FuncMap {
219218
"DisableImportLocal": func() bool {
220219
return !setting.ImportLocalPaths
221220
},
222-
"Dict": func(values ...interface{}) (map[string]interface{}, error) {
223-
if len(values)%2 != 0 {
224-
return nil, errors.New("invalid dict call")
225-
}
226-
dict := make(map[string]interface{}, len(values)/2)
227-
for i := 0; i < len(values); i += 2 {
228-
key, ok := values[i].(string)
229-
if !ok {
230-
return nil, errors.New("dict keys must be strings")
231-
}
232-
dict[key] = values[i+1]
233-
}
234-
return dict, nil
235-
},
236221
"Printf": fmt.Sprintf,
237222
"Escape": Escape,
238223
"Sec2Time": util.SecToTime,
@@ -242,35 +227,7 @@ func NewFuncMap() []template.FuncMap {
242227
"DefaultTheme": func() string {
243228
return setting.UI.DefaultTheme
244229
},
245-
// pass key-value pairs to a partial template which receives them as a dict
246-
"dict": func(values ...interface{}) (map[string]interface{}, error) {
247-
if len(values) == 0 {
248-
return nil, errors.New("invalid dict call")
249-
}
250-
251-
dict := make(map[string]interface{})
252-
return util.MergeInto(dict, values...)
253-
},
254-
/* like dict but merge key-value pairs into the first dict and return it */
255-
"mergeinto": func(root map[string]interface{}, values ...interface{}) (map[string]interface{}, error) {
256-
if len(values) == 0 {
257-
return nil, errors.New("invalid mergeinto call")
258-
}
259-
260-
dict := make(map[string]interface{})
261-
for key, value := range root {
262-
dict[key] = value
263-
}
264-
265-
return util.MergeInto(dict, values...)
266-
},
267-
"percentage": func(n int, values ...int) float32 {
268-
sum := 0
269-
for i := 0; i < len(values); i++ {
270-
sum += values[i]
271-
}
272-
return float32(n) * 100 / float32(sum)
273-
},
230+
"dict": dict,
274231
"CommentMustAsDiff": gitdiff.CommentMustAsDiff,
275232
"MirrorRemoteAddress": mirrorRemoteAddress,
276233
"NotificationSettings": func() map[string]interface{} {
@@ -413,52 +370,13 @@ func NewTextFuncMap() []texttmpl.FuncMap {
413370
},
414371
"EllipsisString": base.EllipsisString,
415372
"URLJoin": util.URLJoin,
416-
"Dict": func(values ...interface{}) (map[string]interface{}, error) {
417-
if len(values)%2 != 0 {
418-
return nil, errors.New("invalid dict call")
419-
}
420-
dict := make(map[string]interface{}, len(values)/2)
421-
for i := 0; i < len(values); i += 2 {
422-
key, ok := values[i].(string)
423-
if !ok {
424-
return nil, errors.New("dict keys must be strings")
425-
}
426-
dict[key] = values[i+1]
427-
}
428-
return dict, nil
429-
},
430-
"Printf": fmt.Sprintf,
431-
"Escape": Escape,
432-
"Sec2Time": util.SecToTime,
373+
"Printf": fmt.Sprintf,
374+
"Escape": Escape,
375+
"Sec2Time": util.SecToTime,
433376
"ParseDeadline": func(deadline string) []string {
434377
return strings.Split(deadline, "|")
435378
},
436-
"dict": func(values ...interface{}) (map[string]interface{}, error) {
437-
if len(values) == 0 {
438-
return nil, errors.New("invalid dict call")
439-
}
440-
441-
dict := make(map[string]interface{})
442-
443-
for i := 0; i < len(values); i++ {
444-
switch key := values[i].(type) {
445-
case string:
446-
i++
447-
if i == len(values) {
448-
return nil, errors.New("specify the key for non array values")
449-
}
450-
dict[key] = values[i]
451-
case map[string]interface{}:
452-
m := values[i].(map[string]interface{})
453-
for i, v := range m {
454-
dict[i] = v
455-
}
456-
default:
457-
return nil, errors.New("dict values must be maps")
458-
}
459-
}
460-
return dict, nil
461-
},
379+
"dict": dict,
462380
"QueryEscape": url.QueryEscape,
463381
"Eval": Eval,
464382
}}

modules/templates/util.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package templates
5+
6+
import (
7+
"fmt"
8+
"reflect"
9+
)
10+
11+
func dictMerge(base map[string]any, arg any) bool {
12+
if arg == nil {
13+
return true
14+
}
15+
rv := reflect.ValueOf(arg)
16+
if rv.Kind() == reflect.Map {
17+
for _, k := range rv.MapKeys() {
18+
base[k.String()] = rv.MapIndex(k).Interface()
19+
}
20+
return true
21+
}
22+
return false
23+
}
24+
25+
// dict is a helper function for creating a map[string]any from a list of key-value pairs.
26+
// If the key is dot ".", the value is merged into the base map, just like Golang template's dot syntax: dot means current
27+
// The dot syntax is highly discouraged because it might cause unclear key conflicts. It's always good to use explicit keys.
28+
func dict(args ...any) (map[string]any, error) {
29+
if len(args)%2 != 0 {
30+
return nil, fmt.Errorf("invalid dict constructor syntax: must have key-value pairs")
31+
}
32+
m := make(map[string]any, len(args)/2)
33+
for i := 0; i < len(args); i += 2 {
34+
key, ok := args[i].(string)
35+
if !ok {
36+
return nil, fmt.Errorf("invalid dict constructor syntax: unable to merge args[%d]", i)
37+
}
38+
if key == "." {
39+
if ok = dictMerge(m, args[i+1]); !ok {
40+
return nil, fmt.Errorf("invalid dict constructor syntax: dot arg[%d] must be followed by a dict", i)
41+
}
42+
} else {
43+
m[key] = args[i+1]
44+
}
45+
}
46+
return m, nil
47+
}

modules/templates/util_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package templates
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestDict(t *testing.T) {
13+
type M map[string]any
14+
cases := []struct {
15+
args []any
16+
want map[string]any
17+
}{
18+
{[]any{"a", 1, "b", 2}, M{"a": 1, "b": 2}},
19+
{[]any{".", M{"base": 1}, "b", 2}, M{"base": 1, "b": 2}},
20+
{[]any{"a", 1, ".", M{"extra": 2}}, M{"a": 1, "extra": 2}},
21+
{[]any{"a", 1, ".", map[string]int{"int": 2}}, M{"a": 1, "int": 2}},
22+
{[]any{".", nil, "b", 2}, M{"b": 2}},
23+
}
24+
25+
for _, c := range cases {
26+
got, err := dict(c.args...)
27+
if assert.NoError(t, err) {
28+
assert.EqualValues(t, c.want, got)
29+
}
30+
}
31+
32+
bads := []struct {
33+
args []any
34+
}{
35+
{[]any{"a", 1, "b"}},
36+
{[]any{1}},
37+
{[]any{struct{}{}}},
38+
}
39+
for _, c := range bads {
40+
_, err := dict(c.args...)
41+
assert.Error(t, err)
42+
}
43+
}

modules/util/util.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package util
66
import (
77
"bytes"
88
"crypto/rand"
9-
"errors"
109
"fmt"
1110
"math/big"
1211
"strconv"
@@ -117,29 +116,6 @@ func NormalizeEOL(input []byte) []byte {
117116
return tmp[:pos]
118117
}
119118

120-
// MergeInto merges pairs of values into a "dict"
121-
func MergeInto(dict map[string]interface{}, values ...interface{}) (map[string]interface{}, error) {
122-
for i := 0; i < len(values); i++ {
123-
switch key := values[i].(type) {
124-
case string:
125-
i++
126-
if i == len(values) {
127-
return nil, errors.New("specify the key for non array values")
128-
}
129-
dict[key] = values[i]
130-
case map[string]interface{}:
131-
m := values[i].(map[string]interface{})
132-
for i, v := range m {
133-
dict[i] = v
134-
}
135-
default:
136-
return nil, errors.New("dict values must be maps")
137-
}
138-
}
139-
140-
return dict, nil
141-
}
142-
143119
// CryptoRandomInt returns a crypto random integer between 0 and limit, inclusive
144120
func CryptoRandomInt(limit int64) (int64, error) {
145121
rInt, err := rand.Int(rand.Reader, big.NewInt(limit))

package-lock.json

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

routers/api/actions/runner/utils.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
actions_model "code.gitea.io/gitea/models/actions"
1111
secret_model "code.gitea.io/gitea/models/secret"
12+
"code.gitea.io/gitea/modules/git"
1213
"code.gitea.io/gitea/modules/json"
1314
"code.gitea.io/gitea/modules/log"
1415
secret_module "code.gitea.io/gitea/modules/secret"
@@ -98,7 +99,7 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
9899
"head_ref": "", // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
99100
"job": fmt.Sprint(t.JobID), // string, The job_id of the current job.
100101
"ref": t.Job.Run.Ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
101-
"ref_name": t.Job.Run.Ref, // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
102+
"ref_name": git.RefEndName(t.Job.Run.Ref), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
102103
"ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
103104
"ref_type": "", // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
104105
"path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."

routers/web/repo/actions/view.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type ViewJob struct {
7575
Name string `json:"name"`
7676
Status string `json:"status"`
7777
CanRerun bool `json:"canRerun"`
78+
Duration string `json:"duration"`
7879
}
7980

8081
type ViewCommit struct {
@@ -144,6 +145,7 @@ func ViewPost(ctx *context_module.Context) {
144145
Name: v.Name,
145146
Status: v.Status.String(),
146147
CanRerun: v.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions),
148+
Duration: v.Duration().String(),
147149
})
148150
}
149151

0 commit comments

Comments
 (0)