Skip to content

Actions Runner rest api #33873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c062f18
WIP ActionRunner Org + Repo Api
ChristopherHX Mar 2, 2025
e2d9774
fix null labels
ChristopherHX Mar 2, 2025
f37caab
fix removed runner could still be retrieved via get
ChristopherHX Mar 2, 2025
0655965
add swagger docu / fix build
ChristopherHX Mar 12, 2025
f1de208
add admin / user level + api add correct registration token endpoint
ChristopherHX Mar 13, 2025
a3b42a9
add tests
ChristopherHX Mar 13, 2025
7a75b1c
update date
ChristopherHX Mar 13, 2025
76c5505
fix lint
ChristopherHX Mar 13, 2025
93d464b
fix definition bug
ChristopherHX Mar 13, 2025
d6db24c
add org test
ChristopherHX Mar 13, 2025
6f8aaf6
fix mssql failure add token_hash
ChristopherHX Mar 13, 2025
1394ed7
Update models/actions/runner.go
ChristopherHX Mar 14, 2025
f581e49
Merge branch 'main' into runners-rest-api
ChristopherHX Mar 14, 2025
e95db5a
Merge branch 'main' into runners-rest-api
ChristopherHX Mar 14, 2025
82834ff
expose ephemeral flag of runners
ChristopherHX Mar 14, 2025
efd080e
add permission org test
ChristopherHX Mar 14, 2025
b8b96d0
add check runner conflict test
ChristopherHX Mar 14, 2025
dfbdec1
Remove OS field, not needed for my usecase
ChristopherHX Mar 14, 2025
f387363
swagger remove OS field
ChristopherHX Mar 14, 2025
ee066b6
format test
ChristopherHX Mar 14, 2025
cb8f30b
remove active runner deletion blocker
ChristopherHX Mar 16, 2025
3b38b05
Merge branch 'main' of https://github.com/go-gitea/gitea into runners…
ChristopherHX Mar 16, 2025
1eed2ec
Repo level tests
ChristopherHX Mar 17, 2025
e9a4ef9
Merge branch 'main' of https://github.com/go-gitea/gitea into runners…
ChristopherHX Mar 17, 2025
307b8e1
add missing fixture
ChristopherHX Mar 17, 2025
72e71c8
Merge branch 'main' of https://github.com/go-gitea/gitea into runners…
ChristopherHX Mar 27, 2025
fb75e23
consistency show all runners in admin endpoint like webui
ChristopherHX Apr 11, 2025
0ea0947
Merge branch 'main' of https://github.com/go-gitea/gitea into pr/Chri…
ChristopherHX Apr 11, 2025
6e79cb0
Merge branch 'main' into runners-rest-api
ChristopherHX Apr 11, 2025
3729e9f
fix test stability
ChristopherHX Apr 11, 2025
a727380
update tests
ChristopherHX Apr 11, 2025
0c222f8
add elements match
ChristopherHX Apr 11, 2025
7ab099a
add comments on expected behavior
ChristopherHX Apr 18, 2025
424ee22
fix FindRunnerOptions comment to match behavior
ChristopherHX Apr 18, 2025
4fb1580
add panic checks, add comments
wxiaoguang Apr 18, 2025
93f32b4
add missing return, improve comment
wxiaoguang Apr 18, 2025
4bfcf22
Merge branch 'main' into runners-rest-api
wxiaoguang Apr 18, 2025
6bf72ef
merge tests
wxiaoguang Apr 18, 2025
d23a644
remove obsolete test DeleteNoConflictWhileJobIsDone
ChristopherHX Apr 18, 2025
ec9df5e
Merge branch 'main' into runners-rest-api
GiteaBot Apr 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions models/actions/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/shared/types"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
Expand Down Expand Up @@ -123,8 +124,15 @@ func (r *ActionRunner) IsOnline() bool {
return false
}

// Editable checks if the runner is editable by the user
func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
// EditableInContext checks if the runner is editable by the "context" owner/repo
// ownerID == 0 and repoID == 0 means "admin" context, any runner including global runners could be edited
// ownerID == 0 and repoID != 0 means "repo" context, any runner belonging to the given repo could be edited
// ownerID != 0 and repoID == 0 means "owner(org/user)" context, any runner belonging to the given user/org could be edited
// ownerID != 0 and repoID != 0 means "owner" OR "repo" context, legacy behavior, but we should forbid using it
func (r *ActionRunner) EditableInContext(ownerID, repoID int64) bool {
if ownerID != 0 && repoID != 0 {
setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
}
if ownerID == 0 && repoID == 0 {
return true
}
Expand Down Expand Up @@ -168,6 +176,12 @@ func init() {
db.RegisterModel(&ActionRunner{})
}

// FindRunnerOptions
// ownerID == 0 and repoID == 0 means any runner including global runners
// repoID != 0 and WithAvailable == false means any runner for the given repo
// repoID != 0 and WithAvailable == true means any runner for the given repo, parent user/org, and global runners
// ownerID != 0 and repoID == 0 and WithAvailable == false means any runner for the given user/org
// ownerID != 0 and repoID == 0 and WithAvailable == true means any runner for the given user/org and global runners
type FindRunnerOptions struct {
db.ListOptions
IDs []int64
Expand Down
40 changes: 40 additions & 0 deletions models/fixtures/action_runner.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-
id: 34346
name: runner_to_be_deleted-user
uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF18
token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF18
version: "1.0.0"
owner_id: 1
repo_id: 0
description: "This runner is going to be deleted"
agent_labels: '["runner_to_be_deleted","linux"]'
-
id: 34347
name: runner_to_be_deleted-org
uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF19
token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF19
version: "1.0.0"
owner_id: 3
repo_id: 0
description: "This runner is going to be deleted"
agent_labels: '["runner_to_be_deleted","linux"]'
-
id: 34348
name: runner_to_be_deleted-repo1
uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF20
token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF20
version: "1.0.0"
owner_id: 0
repo_id: 1
description: "This runner is going to be deleted"
agent_labels: '["runner_to_be_deleted","linux"]'
-
id: 34349
name: runner_to_be_deleted
uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF17
token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF17
version: "1.0.0"
owner_id: 0
repo_id: 0
description: "This runner is going to be deleted"
agent_labels: '["runner_to_be_deleted","linux"]'
23 changes: 23 additions & 0 deletions modules/structs/repo_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,26 @@ type ActionWorkflowJob struct {
// swagger:strfmt date-time
CompletedAt time.Time `json:"completed_at,omitempty"`
}

// ActionRunnerLabel represents a Runner Label
type ActionRunnerLabel struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
}

// ActionRunner represents a Runner
type ActionRunner struct {
ID int64 `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
Busy bool `json:"busy"`
Ephemeral bool `json:"ephemeral"`
Labels []*ActionRunnerLabel `json:"labels"`
}

// ActionRunnersResponse returns Runners
type ActionRunnersResponse struct {
Entries []*ActionRunner `json:"runners"`
TotalCount int64 `json:"total_count"`
}
78 changes: 78 additions & 0 deletions routers/api/v1/admin/runners.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,81 @@ func GetRegistrationToken(ctx *context.APIContext) {

shared.GetRegistrationToken(ctx, 0, 0)
}

// CreateRegistrationToken returns the token to register global runners
func CreateRegistrationToken(ctx *context.APIContext) {
// swagger:operation POST /admin/actions/runners/registration-token admin adminCreateRunnerRegistrationToken
// ---
// summary: Get an global actions runner registration token
// produces:
// - application/json
// parameters:
// responses:
// "200":
// "$ref": "#/responses/RegistrationToken"

shared.GetRegistrationToken(ctx, 0, 0)
}

// ListRunners get all runners
func ListRunners(ctx *context.APIContext) {
// swagger:operation GET /admin/actions/runners admin getAdminRunners
// ---
// summary: Get all runners
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/definitions/ActionRunnersResponse"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListRunners(ctx, 0, 0)
}

// GetRunner get an global runner
func GetRunner(ctx *context.APIContext) {
// swagger:operation GET /admin/actions/runners/{runner_id} admin getAdminRunner
// ---
// summary: Get an global runner
// produces:
// - application/json
// parameters:
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/definitions/ActionRunner"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.GetRunner(ctx, 0, 0, ctx.PathParamInt64("runner_id"))
}

// DeleteRunner delete an global runner
func DeleteRunner(ctx *context.APIContext) {
// swagger:operation DELETE /admin/actions/runners/{runner_id} admin deleteAdminRunner
// ---
// summary: Delete an global runner
// produces:
// - application/json
// parameters:
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// responses:
// "204":
// description: runner has been deleted
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.DeleteRunner(ctx, 0, 0, ctx.PathParamInt64("runner_id"))
}
14 changes: 14 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,11 @@ func Routes() *web.Router {
})

m.Group("/runners", func() {
m.Get("", reqToken(), reqChecker, act.ListRunners)
m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken)
m.Post("/registration-token", reqToken(), reqChecker, act.CreateRegistrationToken)
m.Get("/{runner_id}", reqToken(), reqChecker, act.GetRunner)
m.Delete("/{runner_id}", reqToken(), reqChecker, act.DeleteRunner)
})
})
}
Expand Down Expand Up @@ -1043,7 +1047,11 @@ func Routes() *web.Router {
})

m.Group("/runners", func() {
m.Get("", reqToken(), user.ListRunners)
m.Get("/registration-token", reqToken(), user.GetRegistrationToken)
m.Post("/registration-token", reqToken(), user.CreateRegistrationToken)
m.Get("/{runner_id}", reqToken(), user.GetRunner)
m.Delete("/{runner_id}", reqToken(), user.DeleteRunner)
})
})

Expand Down Expand Up @@ -1689,6 +1697,12 @@ func Routes() *web.Router {
Patch(bind(api.EditHookOption{}), admin.EditHook).
Delete(admin.DeleteHook)
})
m.Group("/actions/runners", func() {
m.Get("", admin.ListRunners)
m.Post("/registration-token", admin.CreateRegistrationToken)
m.Get("/{runner_id}", admin.GetRunner)
m.Delete("/{runner_id}", admin.DeleteRunner)
})
m.Group("/runners", func() {
m.Get("/registration-token", admin.GetRegistrationToken)
})
Expand Down
100 changes: 100 additions & 0 deletions routers/api/v1/org/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,27 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
}

// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
// CreateRegistrationToken returns the token to register org runners
func (Action) CreateRegistrationToken(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/actions/runners/registration-token organization orgCreateRunnerRegistrationToken
// ---
// summary: Get an organization's actions runner registration token
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/RegistrationToken"

shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
}

// ListVariables list org-level variables
func (Action) ListVariables(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
Expand Down Expand Up @@ -470,6 +491,85 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}

// ListRunners get org-level runners
func (Action) ListRunners(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/runners organization getOrgRunners
// ---
// summary: Get org-level runners
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/definitions/ActionRunnersResponse"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListRunners(ctx, ctx.Org.Organization.ID, 0)
}

// GetRunner get an org-level runner
func (Action) GetRunner(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/runners/{runner_id} organization getOrgRunner
// ---
// summary: Get an org-level runner
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/definitions/ActionRunner"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.GetRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
}

// DeleteRunner delete an org-level runner
func (Action) DeleteRunner(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/actions/runners/{runner_id} organization deleteOrgRunner
// ---
// summary: Delete an org-level runner
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// responses:
// "204":
// description: runner has been deleted
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.DeleteRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
}

var _ actions_service.API = new(Action)

// Action implements actions_service.API
Expand Down
Loading