Skip to content

Commit d2d51c4

Browse files
committed
actions from private repos
1 parent 03fce8f commit d2d51c4

File tree

28 files changed

+375
-25
lines changed

28 files changed

+375
-25
lines changed

models/actions/task.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
auth_model "code.gitea.io/gitea/models/auth"
1414
"code.gitea.io/gitea/models/db"
15+
repo_model "code.gitea.io/gitea/models/repo"
1516
"code.gitea.io/gitea/models/unit"
1617
"code.gitea.io/gitea/modules/container"
1718
"code.gitea.io/gitea/modules/log"
@@ -38,9 +39,10 @@ type ActionTask struct {
3839
Started timeutil.TimeStamp `xorm:"index"`
3940
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
4041

41-
RepoID int64 `xorm:"index"`
42-
OwnerID int64 `xorm:"index"`
43-
CommitSHA string `xorm:"index"`
42+
RepoID int64 `xorm:"index"`
43+
Repo *repo_model.Repository `xorm:"-"`
44+
OwnerID int64 `xorm:"index"`
45+
CommitSHA string `xorm:"index"`
4446
IsForkPullRequest bool
4547

4648
Token string `xorm:"-"`
@@ -144,14 +146,22 @@ func (task *ActionTask) LoadAttributes(ctx context.Context) error {
144146
task.Steps = steps
145147
}
146148

147-
return nil
149+
return task.LoadRepository(ctx)
148150
}
149151

150152
func (task *ActionTask) GenerateToken() (err error) {
151153
task.Token, task.TokenSalt, task.TokenHash, task.TokenLastEight, err = generateSaltedToken()
152154
return err
153155
}
154156

157+
func (task *ActionTask) LoadRepository(ctx context.Context) (err error) {
158+
if task.Repo != nil {
159+
return nil
160+
}
161+
task.Repo, err = repo_model.GetRepositoryByID(ctx, task.RepoID)
162+
return err
163+
}
164+
155165
func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) {
156166
var task ActionTask
157167
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&task)

models/fixtures/action_run.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,22 @@
139139
updated: 1683636626
140140
need_approval: 0
141141
approved_by: 0
142+
-
143+
id: 804
144+
title: "use a private action"
145+
repo_id: 6
146+
owner_id: 10
147+
workflow_id: "run.yaml"
148+
index: 189
149+
trigger_user_id: 10
150+
ref: "refs/heads/master"
151+
commit_sha: "6e64b26de7ba966d01d90ecfaf5c7f14ef203e86"
152+
event: "push"
153+
is_fork_pull_request: 0
154+
status: 1
155+
started: 1683636528
156+
stopped: 1683636626
157+
created: 1683636108
158+
updated: 1683636626
159+
need_approval: 0
160+
approved_by: 0

models/fixtures/action_run_job.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,17 @@
129129
status: 5
130130
started: 1683636528
131131
stopped: 1683636626
132+
-
133+
id: 205
134+
run_id: 804
135+
repo_id: 6
136+
owner_id: 10
137+
commit_sha: 6e64b26de7ba966d01d90ecfaf5c7f14ef203e86
138+
is_fork_pull_request: 0
139+
name: job_2
140+
attempt: 1
141+
job_id: job_2
142+
task_id: 48
143+
status: 1
144+
started: 1683636528
145+
stopped: 1683636626

models/fixtures/action_task.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,23 @@
177177
log_length: 0
178178
log_size: 0
179179
log_expired: 0
180+
-
181+
id: 55
182+
job_id: 205
183+
attempt: 1
184+
runner_id: 1
185+
status: 6 # 6 is the status code for "running"
186+
started: 1683636528
187+
stopped: 1683636626
188+
repo_id: 6
189+
owner_id: 10
190+
commit_sha: 6e64b26de7ba966d01d90ecfaf5c7f14ef203e86
191+
is_fork_pull_request: 0
192+
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc478422b
193+
token_salt: ERxJGHvg3I
194+
token_last_eight: 182199eb
195+
log_filename: collaborative-owner-test/1a/49.log
196+
log_in_storage: 1
197+
log_length: 707
198+
log_size: 90179
199+
log_expired: 0

models/fixtures/repo_unit.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,3 +733,10 @@
733733
type: 3
734734
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
735735
created_unix: 946684810
736+
737+
-
738+
id: 111
739+
repo_id: 3
740+
type: 10
741+
config: "{}"
742+
created_unix: 946684810

models/repo/repo_unit.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {
170170

171171
type ActionsConfig struct {
172172
DisabledWorkflows []string
173+
// CollaborativeOwnerIDs is a list of owner IDs used to share actions from private repos.
174+
// Only workflows from the private repos whose owners are in CollaborativeOwnerIDs can access the current repo's actions.
175+
CollaborativeOwnerIDs []int64
173176
}
174177

175178
func (cfg *ActionsConfig) EnableWorkflow(file string) {
@@ -192,6 +195,20 @@ func (cfg *ActionsConfig) DisableWorkflow(file string) {
192195
cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file)
193196
}
194197

198+
func (cfg *ActionsConfig) AddCollaborativeOwner(ownerID int64) {
199+
if !slices.Contains(cfg.CollaborativeOwnerIDs, ownerID) {
200+
cfg.CollaborativeOwnerIDs = append(cfg.CollaborativeOwnerIDs, ownerID)
201+
}
202+
}
203+
204+
func (cfg *ActionsConfig) RemoveCollaborativeOwner(ownerID int64) {
205+
cfg.CollaborativeOwnerIDs = util.SliceRemoveAll(cfg.CollaborativeOwnerIDs, ownerID)
206+
}
207+
208+
func (cfg *ActionsConfig) IsCollaborativeOwner(ownerID int64) bool {
209+
return slices.Contains(cfg.CollaborativeOwnerIDs, ownerID)
210+
}
211+
195212
// FromDB fills up a ActionsConfig from serialized format.
196213
func (cfg *ActionsConfig) FromDB(bs []byte) error {
197214
return json.UnmarshalHandleDoubleEncode(bs, &cfg)

models/user/search.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package user
66
import (
77
"context"
88
"fmt"
9+
"slices"
910
"strings"
1011

1112
"code.gitea.io/gitea/models/db"
@@ -22,7 +23,7 @@ type SearchUserOptions struct {
2223
db.ListOptions
2324

2425
Keyword string
25-
Type UserType
26+
Types []UserType
2627
UID int64
2728
LoginName string // this option should be used only for admin user
2829
SourceID int64 // this option should be used only for admin user
@@ -43,16 +44,16 @@ type SearchUserOptions struct {
4344

4445
func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session {
4546
var cond builder.Cond
46-
cond = builder.Eq{"type": opts.Type}
47+
cond = builder.In("type", opts.Types)
4748
if opts.IncludeReserved {
48-
switch opts.Type {
49-
case UserTypeIndividual:
49+
switch {
50+
case slices.Contains(opts.Types, UserTypeIndividual):
5051
cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or(
5152
builder.Eq{"type": UserTypeBot},
5253
).Or(
5354
builder.Eq{"type": UserTypeRemoteUser},
5455
)
55-
case UserTypeOrganization:
56+
case slices.Contains(opts.Types, UserTypeOrganization):
5657
cond = cond.Or(builder.Eq{"type": UserTypeOrganizationReserved})
5758
}
5859
}

models/user/user.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,3 +1444,15 @@ func DisabledFeaturesWithLoginType(user *User) *container.Set[string] {
14441444
}
14451445
return &setting.Admin.UserDisabledFeatures
14461446
}
1447+
1448+
// GetUserOrOrgIDByName returns the id for a user or an org by name
1449+
func GetUserOrOrgIDByName(ctx context.Context, name string) (int64, error) {
1450+
var id int64
1451+
has, err := db.GetEngine(ctx).Table("user").Where("name = ?", name).Cols("id").Get(&id)
1452+
if err != nil {
1453+
return 0, err
1454+
} else if !has {
1455+
return 0, fmt.Errorf("user or org with name %s: %w", name, util.ErrNotExist)
1456+
}
1457+
return id, nil
1458+
}

models/user/user_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func TestSearchUsers(t *testing.T) {
126126

127127
// test orgs
128128
testOrgSuccess := func(opts user_model.SearchUserOptions, expectedOrgIDs []int64) {
129-
opts.Type = user_model.UserTypeOrganization
129+
opts.Types = []user_model.UserType{user_model.UserTypeOrganization}
130130
testSuccess(opts, expectedOrgIDs)
131131
}
132132

@@ -150,7 +150,7 @@ func TestSearchUsers(t *testing.T) {
150150

151151
// test users
152152
testUserSuccess := func(opts user_model.SearchUserOptions, expectedUserIDs []int64) {
153-
opts.Type = user_model.UserTypeIndividual
153+
opts.Types = []user_model.UserType{user_model.UserTypeIndividual}
154154
testSuccess(opts, expectedUserIDs)
155155
}
156156

options/locale/locale_en-US.ini

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3910,6 +3910,15 @@ variables.update.success = The variable has been edited.
39103910
logs.always_auto_scroll = Always auto scroll logs
39113911
logs.always_expand_running = Always expand running logs
39123912

3913+
general = General
3914+
general.collaborative_owners_management = Collaborative Owners Management
3915+
general.collaborative_owners_management_help = A collaborative owner is a user or an organization whose private repository has access to the actions and workflows of this repository.
3916+
general.add_collaborative_owner = Add Collaborative Owner
3917+
general.collaborative_owner_not_exist = The collaborative owner does not exist.
3918+
general.remove_collaborative_owner = Remove Collaborative Owner
3919+
general.remove_collaborative_owner_desc = Removing a collaborative owner will prevent the repositories of the owner from accessing the actions in this repository. Continue?
3920+
general.collaborative_owner_not_required = The actions and workflows of a public repository are always accessible to other repositories. You do not need to specify collaborative owners.
3921+
39133922
[projects]
39143923
deleted.display_name = Deleted Project
39153924
type-1.display_name = Individual Project

0 commit comments

Comments
 (0)