mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 21:16:26 +01:00 
			
		
		
		
	Actions Runner rest api (#33873)
Implements runner apis based on https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-an-organization - Add Post endpoints for registration-token, google/go-github revealed this as problem - We should deprecate Get Endpoints, leaving them for compatibility - Get endpoint of admin has api path /admin/runners/registration-token that feels wrong, /admin/actions/runners/registration-token seems more consistent with user/org/repo api - Get Runner Api - List Runner Api - Delete Runner Api - Tests admin / user / org / repo level endpoints Related to #33750 (implements point 1 and 2) Via needs discovered in #32461, this runner api is needed to allow cleanup of runners that are deallocated without user interaction. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
							parent
							
								
									ba0deab616
								
							
						
					
					
						commit
						21b43fce08
					
				@ -14,6 +14,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/shared/types"
 | 
						"code.gitea.io/gitea/models/shared/types"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/optional"
 | 
						"code.gitea.io/gitea/modules/optional"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/translation"
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@ -123,8 +124,15 @@ func (r *ActionRunner) IsOnline() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Editable checks if the runner is editable by the user
 | 
					// EditableInContext checks if the runner is editable by the "context" owner/repo
 | 
				
			||||||
func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
 | 
					// 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 {
 | 
						if ownerID == 0 && repoID == 0 {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -168,6 +176,12 @@ func init() {
 | 
				
			|||||||
	db.RegisterModel(&ActionRunner{})
 | 
						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 {
 | 
					type FindRunnerOptions struct {
 | 
				
			||||||
	db.ListOptions
 | 
						db.ListOptions
 | 
				
			||||||
	IDs           []int64
 | 
						IDs           []int64
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										40
									
								
								models/fixtures/action_runner.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								models/fixtures/action_runner.yml
									
									
									
									
									
										Normal file
									
								
							@ -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"]'
 | 
				
			||||||
@ -133,3 +133,26 @@ type ActionWorkflowJob struct {
 | 
				
			|||||||
	// swagger:strfmt date-time
 | 
						// swagger:strfmt date-time
 | 
				
			||||||
	CompletedAt time.Time `json:"completed_at,omitempty"`
 | 
						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"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,3 +24,81 @@ func GetRegistrationToken(ctx *context.APIContext) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	shared.GetRegistrationToken(ctx, 0, 0)
 | 
						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"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -912,7 +912,11 @@ func Routes() *web.Router {
 | 
				
			|||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			m.Group("/runners", func() {
 | 
								m.Group("/runners", func() {
 | 
				
			||||||
 | 
									m.Get("", reqToken(), reqChecker, act.ListRunners)
 | 
				
			||||||
				m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken)
 | 
									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)
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -1043,7 +1047,11 @@ func Routes() *web.Router {
 | 
				
			|||||||
				})
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				m.Group("/runners", func() {
 | 
									m.Group("/runners", func() {
 | 
				
			||||||
 | 
										m.Get("", reqToken(), user.ListRunners)
 | 
				
			||||||
					m.Get("/registration-token", reqToken(), user.GetRegistrationToken)
 | 
										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)
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1689,6 +1697,12 @@ func Routes() *web.Router {
 | 
				
			|||||||
					Patch(bind(api.EditHookOption{}), admin.EditHook).
 | 
										Patch(bind(api.EditHookOption{}), admin.EditHook).
 | 
				
			||||||
					Delete(admin.DeleteHook)
 | 
										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.Group("/runners", func() {
 | 
				
			||||||
				m.Get("/registration-token", admin.GetRegistrationToken)
 | 
									m.Get("/registration-token", admin.GetRegistrationToken)
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
				
			|||||||
@ -190,6 +190,27 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
 | 
				
			|||||||
	shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
 | 
						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
 | 
					// ListVariables list org-level variables
 | 
				
			||||||
func (Action) ListVariables(ctx *context.APIContext) {
 | 
					func (Action) ListVariables(ctx *context.APIContext) {
 | 
				
			||||||
	// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
 | 
						// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
 | 
				
			||||||
@ -470,6 +491,85 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
 | 
				
			|||||||
	ctx.Status(http.StatusNoContent)
 | 
						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)
 | 
					var _ actions_service.API = new(Action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Action implements actions_service.API
 | 
					// Action implements actions_service.API
 | 
				
			||||||
 | 
				
			|||||||
@ -183,7 +183,7 @@ func (Action) DeleteSecret(ctx *context.APIContext) {
 | 
				
			|||||||
	//   required: true
 | 
						//   required: true
 | 
				
			||||||
	// responses:
 | 
						// responses:
 | 
				
			||||||
	//   "204":
 | 
						//   "204":
 | 
				
			||||||
	//     description: delete one secret of the organization
 | 
						//     description: delete one secret of the repository
 | 
				
			||||||
	//   "400":
 | 
						//   "400":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
	//   "404":
 | 
						//   "404":
 | 
				
			||||||
@ -531,6 +531,125 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
 | 
				
			|||||||
	shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
 | 
						shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateRegistrationToken returns the token to register repo runners
 | 
				
			||||||
 | 
					func (Action) CreateRegistrationToken(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation POST /repos/{owner}/{repo}/actions/runners/registration-token repository repoCreateRunnerRegistrationToken
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get a repository's actions runner registration token
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRunners get repo-level runners
 | 
				
			||||||
 | 
					func (Action) ListRunners(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /repos/{owner}/{repo}/actions/runners repository getRepoRunners
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get repo-level runners
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
						//   "400":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
 | 
						//   "404":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
						shared.ListRunners(ctx, 0, ctx.Repo.Repository.ID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRunner get an repo-level runner
 | 
				
			||||||
 | 
					func (Action) GetRunner(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /repos/{owner}/{repo}/actions/runners/{runner_id} repository getRepoRunner
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get an repo-level runner
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   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, 0, ctx.Repo.Repository.ID, ctx.PathParamInt64("runner_id"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteRunner delete an repo-level runner
 | 
				
			||||||
 | 
					func (Action) DeleteRunner(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation DELETE /repos/{owner}/{repo}/actions/runners/{runner_id} repository deleteRepoRunner
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Delete an repo-level runner
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   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, 0, ctx.Repo.Repository.ID, ctx.PathParamInt64("runner_id"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ actions_service.API = new(Action)
 | 
					var _ actions_service.API = new(Action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Action implements actions_service.API
 | 
					// Action implements actions_service.API
 | 
				
			||||||
 | 
				
			|||||||
@ -8,8 +8,13 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	actions_model "code.gitea.io/gitea/models/actions"
 | 
						actions_model "code.gitea.io/gitea/models/actions"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/routers/api/v1/utils"
 | 
				
			||||||
	"code.gitea.io/gitea/services/context"
 | 
						"code.gitea.io/gitea/services/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/services/convert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RegistrationToken is response related to registration token
 | 
					// RegistrationToken is response related to registration token
 | 
				
			||||||
@ -30,3 +35,84 @@ func GetRegistrationToken(ctx *context.APIContext, ownerID, repoID int64) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ctx.JSON(http.StatusOK, RegistrationToken{Token: token.Token})
 | 
						ctx.JSON(http.StatusOK, RegistrationToken{Token: token.Token})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRunners lists runners for api route validated ownerID and repoID
 | 
				
			||||||
 | 
					// ownerID == 0 and repoID == 0 means all runners including global runners, does not appear in sql where clause
 | 
				
			||||||
 | 
					// ownerID == 0 and repoID != 0 means all runners for the given repo
 | 
				
			||||||
 | 
					// ownerID != 0 and repoID == 0 means all runners for the given user/org
 | 
				
			||||||
 | 
					// ownerID != 0 and repoID != 0 undefined behavior
 | 
				
			||||||
 | 
					// Access rights are checked at the API route level
 | 
				
			||||||
 | 
					func ListRunners(ctx *context.APIContext, ownerID, repoID int64) {
 | 
				
			||||||
 | 
						if ownerID != 0 && repoID != 0 {
 | 
				
			||||||
 | 
							setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						runners, total, err := db.FindAndCount[actions_model.ActionRunner](ctx, &actions_model.FindRunnerOptions{
 | 
				
			||||||
 | 
							OwnerID:     ownerID,
 | 
				
			||||||
 | 
							RepoID:      repoID,
 | 
				
			||||||
 | 
							ListOptions: utils.GetListOptions(ctx),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.APIErrorInternal(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res := new(api.ActionRunnersResponse)
 | 
				
			||||||
 | 
						res.TotalCount = total
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res.Entries = make([]*api.ActionRunner, len(runners))
 | 
				
			||||||
 | 
						for i, runner := range runners {
 | 
				
			||||||
 | 
							res.Entries[i] = convert.ToActionRunner(ctx, runner)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.JSON(http.StatusOK, &res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRunner get the runner for api route validated ownerID and repoID
 | 
				
			||||||
 | 
					// ownerID == 0 and repoID == 0 means any runner including global runners
 | 
				
			||||||
 | 
					// ownerID == 0 and repoID != 0 means any runner for the given repo
 | 
				
			||||||
 | 
					// ownerID != 0 and repoID == 0 means any runner for the given user/org
 | 
				
			||||||
 | 
					// ownerID != 0 and repoID != 0 undefined behavior
 | 
				
			||||||
 | 
					// Access rights are checked at the API route level
 | 
				
			||||||
 | 
					func GetRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) {
 | 
				
			||||||
 | 
						if ownerID != 0 && repoID != 0 {
 | 
				
			||||||
 | 
							setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						runner, err := actions_model.GetRunnerByID(ctx, runnerID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.APIErrorNotFound(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !runner.EditableInContext(ownerID, repoID) {
 | 
				
			||||||
 | 
							ctx.APIErrorNotFound("No permission to get this runner")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(http.StatusOK, convert.ToActionRunner(ctx, runner))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteRunner deletes the runner for api route validated ownerID and repoID
 | 
				
			||||||
 | 
					// ownerID == 0 and repoID == 0 means any runner including global runners
 | 
				
			||||||
 | 
					// ownerID == 0 and repoID != 0 means any runner for the given repo
 | 
				
			||||||
 | 
					// ownerID != 0 and repoID == 0 means any runner for the given user/org
 | 
				
			||||||
 | 
					// ownerID != 0 and repoID != 0 undefined behavior
 | 
				
			||||||
 | 
					// Access rights are checked at the API route level
 | 
				
			||||||
 | 
					func DeleteRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) {
 | 
				
			||||||
 | 
						if ownerID != 0 && repoID != 0 {
 | 
				
			||||||
 | 
							setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						runner, err := actions_model.GetRunnerByID(ctx, runnerID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.APIErrorInternal(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !runner.EditableInContext(ownerID, repoID) {
 | 
				
			||||||
 | 
							ctx.APIErrorNotFound("No permission to delete this runner")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = actions_model.DeleteRunner(ctx, runner.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.APIErrorInternal(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Status(http.StatusNoContent)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -457,6 +457,20 @@ type swaggerRepoArtifact struct {
 | 
				
			|||||||
	Body api.ActionArtifact `json:"body"`
 | 
						Body api.ActionArtifact `json:"body"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RunnerList
 | 
				
			||||||
 | 
					// swagger:response RunnerList
 | 
				
			||||||
 | 
					type swaggerRunnerList struct {
 | 
				
			||||||
 | 
						// in:body
 | 
				
			||||||
 | 
						Body api.ActionRunnersResponse `json:"body"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Runner
 | 
				
			||||||
 | 
					// swagger:response Runner
 | 
				
			||||||
 | 
					type swaggerRunner struct {
 | 
				
			||||||
 | 
						// in:body
 | 
				
			||||||
 | 
						Body api.ActionRunner `json:"body"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// swagger:response Compare
 | 
					// swagger:response Compare
 | 
				
			||||||
type swaggerCompare struct {
 | 
					type swaggerCompare struct {
 | 
				
			||||||
	// in:body
 | 
						// in:body
 | 
				
			||||||
 | 
				
			|||||||
@ -24,3 +24,81 @@ func GetRegistrationToken(ctx *context.APIContext) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
 | 
						shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateRegistrationToken returns the token to register user runners
 | 
				
			||||||
 | 
					func CreateRegistrationToken(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation POST /user/actions/runners/registration-token user userCreateRunnerRegistrationToken
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get an user's actions runner registration token
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRunners get user-level runners
 | 
				
			||||||
 | 
					func ListRunners(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /user/actions/runners user getUserRunners
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get user-level runners
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
						//   "400":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
 | 
						//   "404":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
						shared.ListRunners(ctx, ctx.Doer.ID, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRunner get an user-level runner
 | 
				
			||||||
 | 
					func GetRunner(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /user/actions/runners/{runner_id} user getUserRunner
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get an user-level 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, ctx.Doer.ID, 0, ctx.PathParamInt64("runner_id"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteRunner delete an user-level runner
 | 
				
			||||||
 | 
					func DeleteRunner(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation DELETE /user/actions/runners/{runner_id} user deleteUserRunner
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Delete an user-level 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, ctx.Doer.ID, 0, ctx.PathParamInt64("runner_id"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -197,7 +197,7 @@ func RunnersEdit(ctx *context.Context) {
 | 
				
			|||||||
		ctx.ServerError("LoadAttributes", err)
 | 
							ctx.ServerError("LoadAttributes", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !runner.Editable(ownerID, repoID) {
 | 
						if !runner.EditableInContext(ownerID, repoID) {
 | 
				
			||||||
		err = errors.New("no permission to edit this runner")
 | 
							err = errors.New("no permission to edit this runner")
 | 
				
			||||||
		ctx.NotFound(err)
 | 
							ctx.NotFound(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@ -250,7 +250,7 @@ func RunnersEditPost(ctx *context.Context) {
 | 
				
			|||||||
		ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err)
 | 
							ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !runner.Editable(ownerID, repoID) {
 | 
						if !runner.EditableInContext(ownerID, repoID) {
 | 
				
			||||||
		ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to edit this runner"))
 | 
							ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to edit this runner"))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -304,7 +304,7 @@ func RunnerDeletePost(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !runner.Editable(rCtx.OwnerID, rCtx.RepoID) {
 | 
						if !runner.EditableInContext(rCtx.OwnerID, rCtx.RepoID) {
 | 
				
			||||||
		ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to delete this runner"))
 | 
							ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to delete this runner"))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -25,4 +25,12 @@ type API interface {
 | 
				
			|||||||
	UpdateVariable(*context.APIContext)
 | 
						UpdateVariable(*context.APIContext)
 | 
				
			||||||
	// GetRegistrationToken get registration token
 | 
						// GetRegistrationToken get registration token
 | 
				
			||||||
	GetRegistrationToken(*context.APIContext)
 | 
						GetRegistrationToken(*context.APIContext)
 | 
				
			||||||
 | 
						// CreateRegistrationToken get registration token
 | 
				
			||||||
 | 
						CreateRegistrationToken(*context.APIContext)
 | 
				
			||||||
 | 
						// ListRunners list runners
 | 
				
			||||||
 | 
						ListRunners(*context.APIContext)
 | 
				
			||||||
 | 
						// GetRunner get a runner
 | 
				
			||||||
 | 
						GetRunner(*context.APIContext)
 | 
				
			||||||
 | 
						// DeleteRunner delete runner
 | 
				
			||||||
 | 
						DeleteRunner(*context.APIContext)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,8 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
						asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
				
			||||||
	"code.gitea.io/gitea/services/gitdiff"
 | 
						"code.gitea.io/gitea/services/gitdiff"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToEmail convert models.EmailAddress to api.Email
 | 
					// ToEmail convert models.EmailAddress to api.Email
 | 
				
			||||||
@ -252,6 +254,30 @@ func ToActionArtifact(repo *repo_model.Repository, art *actions_model.ActionArti
 | 
				
			|||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ToActionRunner(ctx context.Context, runner *actions_model.ActionRunner) *api.ActionRunner {
 | 
				
			||||||
 | 
						status := runner.Status()
 | 
				
			||||||
 | 
						apiStatus := "offline"
 | 
				
			||||||
 | 
						if runner.IsOnline() {
 | 
				
			||||||
 | 
							apiStatus = "online"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						labels := make([]*api.ActionRunnerLabel, len(runner.AgentLabels))
 | 
				
			||||||
 | 
						for i, label := range runner.AgentLabels {
 | 
				
			||||||
 | 
							labels[i] = &api.ActionRunnerLabel{
 | 
				
			||||||
 | 
								ID:   int64(i),
 | 
				
			||||||
 | 
								Name: label,
 | 
				
			||||||
 | 
								Type: "custom",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &api.ActionRunner{
 | 
				
			||||||
 | 
							ID:        runner.ID,
 | 
				
			||||||
 | 
							Name:      runner.Name,
 | 
				
			||||||
 | 
							Status:    apiStatus,
 | 
				
			||||||
 | 
							Busy:      status == runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE,
 | 
				
			||||||
 | 
							Ephemeral: runner.Ephemeral,
 | 
				
			||||||
 | 
							Labels:    labels,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
 | 
					// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
 | 
				
			||||||
func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification {
 | 
					func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification {
 | 
				
			||||||
	verif := asymkey_service.ParseCommitWithSignature(ctx, c)
 | 
						verif := asymkey_service.ParseCommitWithSignature(ctx, c)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										582
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										582
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							@ -75,6 +75,108 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/admin/actions/runners": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "admin"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get all runners",
 | 
				
			||||||
 | 
					        "operationId": "getAdminRunners",
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/admin/actions/runners/registration-token": {
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "admin"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an global actions runner registration token",
 | 
				
			||||||
 | 
					        "operationId": "adminCreateRunnerRegistrationToken",
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/admin/actions/runners/{runner_id}": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "admin"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an global runner",
 | 
				
			||||||
 | 
					        "operationId": "getAdminRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunner"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "admin"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Delete an global runner",
 | 
				
			||||||
 | 
					        "operationId": "deleteAdminRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "204": {
 | 
				
			||||||
 | 
					            "description": "runner has been deleted"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/admin/cron": {
 | 
					    "/admin/cron": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
@ -1697,6 +1799,38 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/orgs/{org}/actions/runners": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "organization"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get org-level runners",
 | 
				
			||||||
 | 
					        "operationId": "getOrgRunners",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the organization",
 | 
				
			||||||
 | 
					            "name": "org",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/orgs/{org}/actions/runners/registration-token": {
 | 
					    "/orgs/{org}/actions/runners/registration-token": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
@ -1721,6 +1855,106 @@
 | 
				
			|||||||
            "$ref": "#/responses/RegistrationToken"
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "organization"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an organization's actions runner registration token",
 | 
				
			||||||
 | 
					        "operationId": "orgCreateRunnerRegistrationToken",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the organization",
 | 
				
			||||||
 | 
					            "name": "org",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/orgs/{org}/actions/runners/{runner_id}": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "organization"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an org-level runner",
 | 
				
			||||||
 | 
					        "operationId": "getOrgRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the organization",
 | 
				
			||||||
 | 
					            "name": "org",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunner"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "organization"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Delete an org-level runner",
 | 
				
			||||||
 | 
					        "operationId": "deleteOrgRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the organization",
 | 
				
			||||||
 | 
					            "name": "org",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "204": {
 | 
				
			||||||
 | 
					            "description": "runner has been deleted"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "/orgs/{org}/actions/secrets": {
 | 
					    "/orgs/{org}/actions/secrets": {
 | 
				
			||||||
@ -4331,6 +4565,45 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/repos/{owner}/{repo}/actions/runners": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "repository"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get repo-level runners",
 | 
				
			||||||
 | 
					        "operationId": "getRepoRunners",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/repos/{owner}/{repo}/actions/runners/registration-token": {
 | 
					    "/repos/{owner}/{repo}/actions/runners/registration-token": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
@ -4362,6 +4635,127 @@
 | 
				
			|||||||
            "$ref": "#/responses/RegistrationToken"
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "repository"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get a repository's actions runner registration token",
 | 
				
			||||||
 | 
					        "operationId": "repoCreateRunnerRegistrationToken",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/repos/{owner}/{repo}/actions/runners/{runner_id}": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "repository"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an repo-level runner",
 | 
				
			||||||
 | 
					        "operationId": "getRepoRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunner"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "repository"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Delete an repo-level runner",
 | 
				
			||||||
 | 
					        "operationId": "deleteRepoRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "204": {
 | 
				
			||||||
 | 
					            "description": "runner has been deleted"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "/repos/{owner}/{repo}/actions/runs/{run}/artifacts": {
 | 
					    "/repos/{owner}/{repo}/actions/runs/{run}/artifacts": {
 | 
				
			||||||
@ -4559,7 +4953,7 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "responses": {
 | 
					        "responses": {
 | 
				
			||||||
          "204": {
 | 
					          "204": {
 | 
				
			||||||
            "description": "delete one secret of the organization"
 | 
					            "description": "delete one secret of the repository"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "400": {
 | 
					          "400": {
 | 
				
			||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
@ -16869,6 +17263,29 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/user/actions/runners": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "user"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get user-level runners",
 | 
				
			||||||
 | 
					        "operationId": "getUserRunners",
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/user/actions/runners/registration-token": {
 | 
					    "/user/actions/runners/registration-token": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
@ -16884,6 +17301,83 @@
 | 
				
			|||||||
            "$ref": "#/responses/RegistrationToken"
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "user"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an user's actions runner registration token",
 | 
				
			||||||
 | 
					        "operationId": "userCreateRunnerRegistrationToken",
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/RegistrationToken"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/user/actions/runners/{runner_id}": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "user"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get an user-level runner",
 | 
				
			||||||
 | 
					        "operationId": "getUserRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunner"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "user"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Delete an user-level runner",
 | 
				
			||||||
 | 
					        "operationId": "deleteUserRunner",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "id of the runner",
 | 
				
			||||||
 | 
					            "name": "runner_id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "204": {
 | 
				
			||||||
 | 
					            "description": "runner has been deleted"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "400": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "/user/actions/secrets/{secretname}": {
 | 
					    "/user/actions/secrets/{secretname}": {
 | 
				
			||||||
@ -19377,6 +19871,80 @@
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "ActionRunner": {
 | 
				
			||||||
 | 
					      "description": "ActionRunner represents a Runner",
 | 
				
			||||||
 | 
					      "type": "object",
 | 
				
			||||||
 | 
					      "properties": {
 | 
				
			||||||
 | 
					        "busy": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "Busy"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "ephemeral": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "Ephemeral"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "id": {
 | 
				
			||||||
 | 
					          "type": "integer",
 | 
				
			||||||
 | 
					          "format": "int64",
 | 
				
			||||||
 | 
					          "x-go-name": "ID"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "labels": {
 | 
				
			||||||
 | 
					          "type": "array",
 | 
				
			||||||
 | 
					          "items": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunnerLabel"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "x-go-name": "Labels"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "name": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "x-go-name": "Name"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "status": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "x-go-name": "Status"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "ActionRunnerLabel": {
 | 
				
			||||||
 | 
					      "description": "ActionRunnerLabel represents a Runner Label",
 | 
				
			||||||
 | 
					      "type": "object",
 | 
				
			||||||
 | 
					      "properties": {
 | 
				
			||||||
 | 
					        "id": {
 | 
				
			||||||
 | 
					          "type": "integer",
 | 
				
			||||||
 | 
					          "format": "int64",
 | 
				
			||||||
 | 
					          "x-go-name": "ID"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "name": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "x-go-name": "Name"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "type": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "x-go-name": "Type"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "ActionRunnersResponse": {
 | 
				
			||||||
 | 
					      "description": "ActionRunnersResponse returns Runners",
 | 
				
			||||||
 | 
					      "type": "object",
 | 
				
			||||||
 | 
					      "properties": {
 | 
				
			||||||
 | 
					        "runners": {
 | 
				
			||||||
 | 
					          "type": "array",
 | 
				
			||||||
 | 
					          "items": {
 | 
				
			||||||
 | 
					            "$ref": "#/definitions/ActionRunner"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "x-go-name": "Entries"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "total_count": {
 | 
				
			||||||
 | 
					          "type": "integer",
 | 
				
			||||||
 | 
					          "format": "int64",
 | 
				
			||||||
 | 
					          "x-go-name": "TotalCount"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "ActionTask": {
 | 
					    "ActionTask": {
 | 
				
			||||||
      "description": "ActionTask represents a ActionTask",
 | 
					      "description": "ActionTask represents a ActionTask",
 | 
				
			||||||
      "type": "object",
 | 
					      "type": "object",
 | 
				
			||||||
@ -27409,6 +27977,18 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "Runner": {
 | 
				
			||||||
 | 
					      "description": "Runner",
 | 
				
			||||||
 | 
					      "schema": {
 | 
				
			||||||
 | 
					        "$ref": "#/definitions/ActionRunner"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "RunnerList": {
 | 
				
			||||||
 | 
					      "description": "RunnerList",
 | 
				
			||||||
 | 
					      "schema": {
 | 
				
			||||||
 | 
					        "$ref": "#/definitions/ActionRunnersResponse"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "SearchResults": {
 | 
					    "SearchResults": {
 | 
				
			||||||
      "description": "SearchResults",
 | 
					      "description": "SearchResults",
 | 
				
			||||||
      "schema": {
 | 
					      "schema": {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										332
									
								
								tests/integration/api_actions_runner_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								tests/integration/api_actions_runner_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,332 @@
 | 
				
			|||||||
 | 
					// Copyright 2025 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package integration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auth_model "code.gitea.io/gitea/models/auth"
 | 
				
			||||||
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/tests"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIActionsRunner(t *testing.T) {
 | 
				
			||||||
 | 
						t.Run("AdminRunner", testActionsRunnerAdmin)
 | 
				
			||||||
 | 
						t.Run("UserRunner", testActionsRunnerUser)
 | 
				
			||||||
 | 
						t.Run("OwnerRunner", testActionsRunnerOwner)
 | 
				
			||||||
 | 
						t.Run("RepoRunner", testActionsRunnerRepo)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testActionsRunnerAdmin(t *testing.T) {
 | 
				
			||||||
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
						adminUsername := "user1"
 | 
				
			||||||
 | 
						token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
 | 
				
			||||||
 | 
						req := NewRequest(t, "POST", "/api/v1/admin/actions/runners/registration-token").AddTokenAuth(token)
 | 
				
			||||||
 | 
						tokenResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var registrationToken struct {
 | 
				
			||||||
 | 
							Token string `json:"token"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						DecodeJSON(t, tokenResp, ®istrationToken)
 | 
				
			||||||
 | 
						assert.NotEmpty(t, registrationToken.Token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", "/api/v1/admin/actions/runners").AddTokenAuth(token)
 | 
				
			||||||
 | 
						runnerListResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						runnerList := api.ActionRunnersResponse{}
 | 
				
			||||||
 | 
						DecodeJSON(t, runnerListResp, &runnerList)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Len(t, runnerList.Entries, 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						idx := slices.IndexFunc(runnerList.Entries, func(e *api.ActionRunner) bool { return e.ID == 34349 })
 | 
				
			||||||
 | 
						require.NotEqual(t, -1, idx)
 | 
				
			||||||
 | 
						expectedRunner := runnerList.Entries[idx]
 | 
				
			||||||
 | 
						assert.Equal(t, "runner_to_be_deleted", expectedRunner.Name)
 | 
				
			||||||
 | 
						assert.False(t, expectedRunner.Ephemeral)
 | 
				
			||||||
 | 
						assert.Len(t, expectedRunner.Labels, 2)
 | 
				
			||||||
 | 
						assert.Equal(t, "runner_to_be_deleted", expectedRunner.Labels[0].Name)
 | 
				
			||||||
 | 
						assert.Equal(t, "linux", expectedRunner.Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify all returned runners can be requested and deleted
 | 
				
			||||||
 | 
						for _, runnerEntry := range runnerList.Entries {
 | 
				
			||||||
 | 
							// Verify get the runner by id
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/admin/actions/runners/%d", runnerEntry.ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							runner := api.ActionRunner{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerResp, &runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, runnerEntry.Name, runner.Name)
 | 
				
			||||||
 | 
							assert.Equal(t, runnerEntry.ID, runner.ID)
 | 
				
			||||||
 | 
							assert.Equal(t, runnerEntry.Ephemeral, runner.Ephemeral)
 | 
				
			||||||
 | 
							assert.ElementsMatch(t, runnerEntry.Labels, runner.Labels)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/admin/actions/runners/%d", runnerEntry.ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify runner deletion
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/admin/actions/runners/%d", runnerEntry.ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testActionsRunnerUser(t *testing.T) {
 | 
				
			||||||
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
						userUsername := "user1"
 | 
				
			||||||
 | 
						token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteUser)
 | 
				
			||||||
 | 
						req := NewRequest(t, "POST", "/api/v1/user/actions/runners/registration-token").AddTokenAuth(token)
 | 
				
			||||||
 | 
						tokenResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var registrationToken struct {
 | 
				
			||||||
 | 
							Token string `json:"token"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						DecodeJSON(t, tokenResp, ®istrationToken)
 | 
				
			||||||
 | 
						assert.NotEmpty(t, registrationToken.Token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", "/api/v1/user/actions/runners").AddTokenAuth(token)
 | 
				
			||||||
 | 
						runnerListResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						runnerList := api.ActionRunnersResponse{}
 | 
				
			||||||
 | 
						DecodeJSON(t, runnerListResp, &runnerList)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Len(t, runnerList.Entries, 1)
 | 
				
			||||||
 | 
						assert.Equal(t, "runner_to_be_deleted-user", runnerList.Entries[0].Name)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(34346), runnerList.Entries[0].ID)
 | 
				
			||||||
 | 
						assert.False(t, runnerList.Entries[0].Ephemeral)
 | 
				
			||||||
 | 
						assert.Len(t, runnerList.Entries[0].Labels, 2)
 | 
				
			||||||
 | 
						assert.Equal(t, "runner_to_be_deleted", runnerList.Entries[0].Labels[0].Name)
 | 
				
			||||||
 | 
						assert.Equal(t, "linux", runnerList.Entries[0].Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify get the runner by id
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
						runnerResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runner := api.ActionRunner{}
 | 
				
			||||||
 | 
						DecodeJSON(t, runnerResp, &runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(t, "runner_to_be_deleted-user", runner.Name)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(34346), runner.ID)
 | 
				
			||||||
 | 
						assert.False(t, runner.Ephemeral)
 | 
				
			||||||
 | 
						assert.Len(t, runner.Labels, 2)
 | 
				
			||||||
 | 
						assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
 | 
				
			||||||
 | 
						assert.Equal(t, "linux", runner.Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify delete the runner by id
 | 
				
			||||||
 | 
						req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify runner deletion
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testActionsRunnerOwner(t *testing.T) {
 | 
				
			||||||
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetRunner", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
 | 
				
			||||||
 | 
							// Verify get the runner by id with read scope
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34347)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							runner := api.ActionRunner{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerResp, &runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted-org", runner.Name)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(34347), runner.ID)
 | 
				
			||||||
 | 
							assert.False(t, runner.Ephemeral)
 | 
				
			||||||
 | 
							assert.Len(t, runner.Labels, 2)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, "linux", runner.Labels[1].Name)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Access", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteOrganization)
 | 
				
			||||||
 | 
							req := NewRequest(t, "POST", "/api/v1/orgs/org3/actions/runners/registration-token").AddTokenAuth(token)
 | 
				
			||||||
 | 
							tokenResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							var registrationToken struct {
 | 
				
			||||||
 | 
								Token string `json:"token"`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							DecodeJSON(t, tokenResp, ®istrationToken)
 | 
				
			||||||
 | 
							assert.NotEmpty(t, registrationToken.Token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", "/api/v1/orgs/org3/actions/runners").AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerListResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							runnerList := api.ActionRunnersResponse{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerListResp, &runnerList)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Len(t, runnerList.Entries, 1)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted-org", runnerList.Entries[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(34347), runnerList.Entries[0].ID)
 | 
				
			||||||
 | 
							assert.False(t, runnerList.Entries[0].Ephemeral)
 | 
				
			||||||
 | 
							assert.Len(t, runnerList.Entries[0].Labels, 2)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted", runnerList.Entries[0].Labels[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, "linux", runnerList.Entries[0].Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify get the runner by id
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							runner := api.ActionRunner{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerResp, &runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted-org", runner.Name)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(34347), runner.ID)
 | 
				
			||||||
 | 
							assert.False(t, runner.Ephemeral)
 | 
				
			||||||
 | 
							assert.Len(t, runner.Labels, 2)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, "linux", runner.Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify delete the runner by id
 | 
				
			||||||
 | 
							req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify runner deletion
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("DeleteReadScopeForbidden", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify delete the runner by id is forbidden with read scope
 | 
				
			||||||
 | 
							req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34347)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetRepoScopeForbidden", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
 | 
				
			||||||
 | 
							// Verify get the runner by id with read scope
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34347)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetAdminRunner", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
 | 
				
			||||||
 | 
							// Verify get a runner by id of different entity is not found
 | 
				
			||||||
 | 
							// runner.EditableInContext(ownerID, repoID) false
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34349)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("DeleteAdminRunner", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteOrganization)
 | 
				
			||||||
 | 
							// Verify delete a runner by id of different entity is not found
 | 
				
			||||||
 | 
							// runner.EditableInContext(ownerID, repoID) false
 | 
				
			||||||
 | 
							req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34349)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testActionsRunnerRepo(t *testing.T) {
 | 
				
			||||||
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetRunner", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
 | 
				
			||||||
 | 
							// Verify get the runner by id with read scope
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34348)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							runner := api.ActionRunner{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerResp, &runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted-repo1", runner.Name)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(34348), runner.ID)
 | 
				
			||||||
 | 
							assert.False(t, runner.Ephemeral)
 | 
				
			||||||
 | 
							assert.Len(t, runner.Labels, 2)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, "linux", runner.Labels[1].Name)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Access", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteRepository)
 | 
				
			||||||
 | 
							req := NewRequest(t, "POST", "/api/v1/repos/user2/repo1/actions/runners/registration-token").AddTokenAuth(token)
 | 
				
			||||||
 | 
							tokenResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							var registrationToken struct {
 | 
				
			||||||
 | 
								Token string `json:"token"`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							DecodeJSON(t, tokenResp, ®istrationToken)
 | 
				
			||||||
 | 
							assert.NotEmpty(t, registrationToken.Token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/actions/runners").AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerListResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							runnerList := api.ActionRunnersResponse{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerListResp, &runnerList)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Len(t, runnerList.Entries, 1)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted-repo1", runnerList.Entries[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(34348), runnerList.Entries[0].ID)
 | 
				
			||||||
 | 
							assert.False(t, runnerList.Entries[0].Ephemeral)
 | 
				
			||||||
 | 
							assert.Len(t, runnerList.Entries[0].Labels, 2)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted", runnerList.Entries[0].Labels[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, "linux", runnerList.Entries[0].Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify get the runner by id
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							runnerResp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							runner := api.ActionRunner{}
 | 
				
			||||||
 | 
							DecodeJSON(t, runnerResp, &runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted-repo1", runner.Name)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(34348), runner.ID)
 | 
				
			||||||
 | 
							assert.False(t, runner.Ephemeral)
 | 
				
			||||||
 | 
							assert.Len(t, runner.Labels, 2)
 | 
				
			||||||
 | 
							assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
 | 
				
			||||||
 | 
							assert.Equal(t, "linux", runner.Labels[1].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify delete the runner by id
 | 
				
			||||||
 | 
							req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify runner deletion
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("DeleteReadScopeForbidden", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Verify delete the runner by id is forbidden with read scope
 | 
				
			||||||
 | 
							req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34348)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetOrganizationScopeForbidden", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
 | 
				
			||||||
 | 
							// Verify get the runner by id with read scope
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34348)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetAdminRunnerNotFound", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
 | 
				
			||||||
 | 
							// Verify get a runner by id of different entity is not found
 | 
				
			||||||
 | 
							// runner.EditableInContext(ownerID, repoID) false
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34349)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("DeleteAdminRunnerNotFound", func(t *testing.T) {
 | 
				
			||||||
 | 
							userUsername := "user2"
 | 
				
			||||||
 | 
							token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteRepository)
 | 
				
			||||||
 | 
							// Verify delete a runner by id of different entity is not found
 | 
				
			||||||
 | 
							// runner.EditableInContext(ownerID, repoID) false
 | 
				
			||||||
 | 
							req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34349)).AddTokenAuth(token)
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user