feat: add support for ephemeral runners compatible with autoscaling tools (#9409)
PR for #9407 Endpoints compliant with github api spec: https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28 ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [ ] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Co-authored-by: Manuel Ganter <manuel.ganter@think-ahead.tech> Co-authored-by: Martin McCaffery <martin.mccaffery@think-ahead.tech> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9409 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Daniel Sy <Daniel.Sy@telekom.de> Co-committed-by: Daniel Sy <Daniel.Sy@telekom.de>
This commit is contained in:
parent
187ad99f3c
commit
7939521a10
13 changed files with 1245 additions and 2 deletions
|
@ -32,3 +32,27 @@ type ActionTaskResponse struct {
|
|||
Entries []*ActionTask `json:"workflow_runs"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
// currently unused as forgejo does not support ephemeral runners, but they are defined in gh api spec
|
||||
Ephemeral bool `json:"ephemeral"`
|
||||
Labels []*ActionRunnerLabel `json:"labels"`
|
||||
}
|
||||
|
||||
// ActionRunnersResponse returns Runners
|
||||
type ActionRunnersResponse struct {
|
||||
Entries []*ActionRunner `json:"runners"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
func GetRegistrationToken(ctx *context.APIContext) {
|
||||
// swagger:operation GET /admin/runners/registration-token admin adminGetRunnerRegistrationToken
|
||||
// ---
|
||||
// summary: Get an global actions runner registration token
|
||||
// summary: Get a global actions runner registration token
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
|
@ -44,3 +44,81 @@ func SearchActionRunJobs(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/forbidden"
|
||||
shared.GetActionRunJobs(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 a 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 a global runner
|
||||
func GetRunner(ctx *context.APIContext) {
|
||||
// swagger:operation GET /admin/actions/runners/{runner_id} admin getAdminRunner
|
||||
// ---
|
||||
// summary: Get a 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.ParamsInt64("runner_id"))
|
||||
}
|
||||
|
||||
// DeleteRunner delete a global runner
|
||||
func DeleteRunner(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /admin/actions/runners/{runner_id} admin deleteAdminRunner
|
||||
// ---
|
||||
// summary: Delete a 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.ParamsInt64("runner_id"))
|
||||
}
|
||||
|
|
|
@ -856,7 +856,11 @@ func Routes() *web.Route {
|
|||
})
|
||||
|
||||
m.Group("/runners", func() {
|
||||
m.Get("", reqToken(), reqChecker, act.ListRunners)
|
||||
m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken)
|
||||
m.Post("/registration-token", reqToken(), reqChecker, act.CreateRegistrationToken)
|
||||
m.Get("/{runner_id}", reqToken(), reqChecker, act.GetRunner)
|
||||
m.Delete("/{runner_id}", reqToken(), reqChecker, act.DeleteRunner)
|
||||
m.Get("/jobs", reqToken(), reqChecker, act.SearchActionRunJobs)
|
||||
})
|
||||
})
|
||||
|
@ -1014,7 +1018,11 @@ func Routes() *web.Route {
|
|||
})
|
||||
|
||||
m.Group("/runners", func() {
|
||||
m.Get("", reqToken(), user.ListRunners)
|
||||
m.Get("/registration-token", reqToken(), user.GetRegistrationToken)
|
||||
m.Post("/registration-token", reqToken(), user.CreateRegistrationToken)
|
||||
m.Get("/{runner_id}", reqToken(), user.GetRunner)
|
||||
m.Delete("/{runner_id}", reqToken(), user.DeleteRunner)
|
||||
m.Get("/jobs", reqToken(), user.SearchActionRunJobs)
|
||||
})
|
||||
})
|
||||
|
@ -1689,6 +1697,12 @@ func Routes() *web.Route {
|
|||
Patch(bind(api.EditHookOption{}), admin.EditHook).
|
||||
Delete(admin.DeleteHook)
|
||||
})
|
||||
m.Group("/actions/runners", func() {
|
||||
m.Get("", admin.ListRunners)
|
||||
m.Post("/registration-token", admin.CreateRegistrationToken)
|
||||
m.Get("/{runner_id}", admin.GetRunner)
|
||||
m.Delete("/{runner_id}", admin.DeleteRunner)
|
||||
})
|
||||
m.Group("/runners", func() {
|
||||
m.Get("/registration-token", admin.GetRegistrationToken)
|
||||
m.Get("/jobs", admin.SearchActionRunJobs)
|
||||
|
|
|
@ -214,6 +214,27 @@ func (Action) SearchActionRunJobs(ctx *context.APIContext) {
|
|||
shared.GetActionRunJobs(ctx, ctx.Org.Organization.ID, 0)
|
||||
}
|
||||
|
||||
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
|
||||
// CreateRegistrationToken returns the token to register org runners
|
||||
func (Action) CreateRegistrationToken(ctx *context.APIContext) {
|
||||
// swagger:operation POST /orgs/{org}/actions/runners/registration-token organization orgCreateRunnerRegistrationToken
|
||||
// ---
|
||||
// summary: Get an organization's actions runner registration token
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: org
|
||||
// in: path
|
||||
// description: name of the organization
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RegistrationToken"
|
||||
|
||||
shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
|
||||
}
|
||||
|
||||
// ListVariables list org-level variables
|
||||
func (Action) ListVariables(ctx *context.APIContext) {
|
||||
// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
|
||||
|
@ -266,6 +287,85 @@ func (Action) ListVariables(ctx *context.APIContext) {
|
|||
ctx.JSON(http.StatusOK, variables)
|
||||
}
|
||||
|
||||
// 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.ParamsInt64("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.ParamsInt64("runner_id"))
|
||||
}
|
||||
|
||||
// GetVariable gives organization's variable
|
||||
func (Action) GetVariable(ctx *context.APIContext) {
|
||||
// swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable
|
||||
|
|
|
@ -504,6 +504,125 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
|
|||
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 a repo-level runner
|
||||
func (Action) GetRunner(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/actions/runners/{runner_id} repository getRepoRunner
|
||||
// ---
|
||||
// summary: Get a 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.ParamsInt64("runner_id"))
|
||||
}
|
||||
|
||||
// DeleteRunner delete a repo-level runner
|
||||
func (Action) DeleteRunner(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/actions/runners/{runner_id} repository deleteRepoRunner
|
||||
// ---
|
||||
// summary: Delete a 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.ParamsInt64("runner_id"))
|
||||
}
|
||||
|
||||
// SearchActionRunJobs return a list of actions jobs filtered by the provided parameters
|
||||
func (Action) SearchActionRunJobs(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/actions/runners/jobs repository repoSearchRunJobs
|
||||
|
|
|
@ -5,6 +5,7 @@ package shared
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -12,7 +13,9 @@ import (
|
|||
"forgejo.org/models/db"
|
||||
"forgejo.org/modules/structs"
|
||||
"forgejo.org/modules/util"
|
||||
"forgejo.org/routers/api/v1/utils"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/services/convert"
|
||||
)
|
||||
|
||||
// RegistrationToken is a string used to register a runner with a server
|
||||
|
@ -69,3 +72,95 @@ func fromRunJobModelToResponse(job []*actions_model.ActionRunJob, labels []strin
|
|||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("ownerID and repoID should not be both set: %d and %d", ownerID, repoID))
|
||||
return
|
||||
}
|
||||
runners, total, err := db.FindAndCount[actions_model.ActionRunner](ctx, &actions_model.FindRunnerOptions{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindCountRunners", map[string]string{})
|
||||
return
|
||||
}
|
||||
|
||||
res := new(structs.ActionRunnersResponse)
|
||||
res.TotalCount = total
|
||||
|
||||
res.Entries = make([]*structs.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 {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("ownerID and repoID should not be both set: %d and %d", ownerID, repoID))
|
||||
return
|
||||
}
|
||||
runner, err := actions_model.GetRunnerByID(ctx, runnerID)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Error(http.StatusNotFound, "GetRunnerNotFound", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRunnerFailed", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !runner.Editable(ownerID, repoID) {
|
||||
ctx.Error(http.StatusNotFound, "RunnerEdit", "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 {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("ownerID and repoID should not be both set: %d and %d", ownerID, repoID))
|
||||
return
|
||||
}
|
||||
runner, err := actions_model.GetRunnerByID(ctx, runnerID)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Error(http.StatusNotFound, "DeleteRunnerNotFound", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteRunnerFailed", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !runner.Editable(ownerID, repoID) {
|
||||
ctx.Error(http.StatusNotFound, "EditRunner", "No permission to delete this runner")
|
||||
return
|
||||
}
|
||||
|
||||
err = actions_model.DeleteRunner(ctx, runner)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
|
|
@ -56,3 +56,17 @@ type swaggerRegistrationToken struct {
|
|||
// in: body
|
||||
Body shared.RegistrationToken `json:"body"`
|
||||
}
|
||||
|
||||
// ActionRunner represents a Runner
|
||||
// swagger:response ActionRunner
|
||||
type swaggerActionRunner struct {
|
||||
// in: body
|
||||
Body api.ActionRunner `json:"body"`
|
||||
}
|
||||
|
||||
// ActionRunnersResponse returns Runners
|
||||
// swagger:response ActionRunnersResponse
|
||||
type swaggerActionRunnerResponse struct {
|
||||
// in: body
|
||||
Body api.ActionRunnersResponse `json:"body"`
|
||||
}
|
||||
|
|
|
@ -50,3 +50,89 @@ func SearchActionRunJobs(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/forbidden"
|
||||
shared.GetActionRunJobs(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"
|
||||
// "401":
|
||||
// "$ref": "#/responses/unauthorized"
|
||||
|
||||
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": "#/responses/ActionRunnersResponse"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "401":
|
||||
// "$ref": "#/responses/unauthorized"
|
||||
// "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": "#/responses/ActionRunner"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "401":
|
||||
// "$ref": "#/responses/unauthorized"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
shared.GetRunner(ctx, ctx.Doer.ID, 0, ctx.ParamsInt64("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"
|
||||
// "401":
|
||||
// "$ref": "#/responses/unauthorized"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
shared.DeleteRunner(ctx, ctx.Doer.ID, 0, ctx.ParamsInt64("runner_id"))
|
||||
}
|
||||
|
|
|
@ -27,4 +27,12 @@ type API interface {
|
|||
GetRegistrationToken(*context.APIContext)
|
||||
// SearchActionRunJobs get pending Action run jobs
|
||||
SearchActionRunJobs(*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)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import (
|
|||
api "forgejo.org/modules/structs"
|
||||
"forgejo.org/modules/util"
|
||||
"forgejo.org/services/gitdiff"
|
||||
|
||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||
)
|
||||
|
||||
// ToEmail convert models.EmailAddress to api.Email
|
||||
|
@ -508,3 +510,27 @@ func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit stri
|
|||
|
||||
return file
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
595
templates/swagger/v1_json.tmpl
generated
595
templates/swagger/v1_json.tmpl
generated
|
@ -267,6 +267,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 a 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 a 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 a 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": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -1127,7 +1229,7 @@
|
|||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "Get an global actions runner registration token",
|
||||
"summary": "Get a global actions runner registration token",
|
||||
"operationId": "adminGetRunnerRegistrationToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
@ -2413,6 +2515,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/jobs": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -2472,6 +2606,106 @@
|
|||
"$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": {
|
||||
|
@ -4979,6 +5213,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/jobs": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -5052,6 +5325,127 @@
|
|||
"$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 a 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 a 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": {
|
||||
|
@ -18294,6 +18688,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/user/actions/runners": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get user-level runners",
|
||||
"operationId": "getUserRunners",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActionRunnersResponse"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/responses/error"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/responses/unauthorized"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/actions/runners/jobs": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -18346,6 +18766,92 @@
|
|||
"$ref": "#/responses/forbidden"
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get an user's actions runner registration token",
|
||||
"operationId": "userCreateRunnerRegistrationToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/RegistrationToken"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/responses/unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/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": "#/responses/ActionRunner"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/responses/error"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/responses/unauthorized"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/responses/unauthorized"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/actions/secrets/{secretname}": {
|
||||
|
@ -21585,6 +22091,81 @@
|
|||
},
|
||||
"x-go-package": "forgejo.org/modules/structs"
|
||||
},
|
||||
"ActionRunner": {
|
||||
"description": "ActionRunner represents a Runner",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"busy": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "Busy"
|
||||
},
|
||||
"ephemeral": {
|
||||
"description": "currently unused as forgejo does not support ephemeral runners, but they are defined in gh api spec",
|
||||
"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": "forgejo.org/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": "forgejo.org/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": "forgejo.org/modules/structs"
|
||||
},
|
||||
"ActionTask": {
|
||||
"description": "ActionTask represents a ActionTask",
|
||||
"type": "object",
|
||||
|
@ -29259,6 +29840,18 @@
|
|||
"$ref": "#/definitions/ListActionRunResponse"
|
||||
}
|
||||
},
|
||||
"ActionRunner": {
|
||||
"description": "ActionRunner represents a Runner",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ActionRunner"
|
||||
}
|
||||
},
|
||||
"ActionRunnersResponse": {
|
||||
"description": "ActionRunnersResponse returns Runners",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ActionRunnersResponse"
|
||||
}
|
||||
},
|
||||
"ActionVariable": {
|
||||
"description": "ActionVariable",
|
||||
"schema": {
|
||||
|
|
|
@ -174,6 +174,36 @@ jobs:
|
|||
})
|
||||
}
|
||||
|
||||
func TestRunnerLifecycleGithubEndpoints(t *testing.T) {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
// registering a mock runner when using a database other than SQLite leaves leftovers
|
||||
t.Skip()
|
||||
}
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
session := loginUser(t, user2.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
apiRepo := createActionsTestRepo(t, token, "actions-runner-registration-with-get", false)
|
||||
runner := newMockRunner()
|
||||
runner.registerAsRepoRunnerWithPost(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"})
|
||||
runnersList := runner.listRunners(t, user2.Name, apiRepo.Name)
|
||||
|
||||
assert.NotNil(t, runnersList)
|
||||
assert.Len(t, runnersList.Entries, 1)
|
||||
assert.Equal(t, "mock-runner", runnersList.Entries[0].Name)
|
||||
|
||||
runnerDetails := runner.getRunner(t, user2.Name, apiRepo.Name, runnersList.Entries[0].ID)
|
||||
assert.Equal(t, "mock-runner", runnerDetails.Name)
|
||||
assert.Equal(t, runnersList.Entries[0].ID, runnerDetails.ID)
|
||||
|
||||
runner.deleteRunner(t, user2.Name, apiRepo.Name, runnersList.Entries[0].ID)
|
||||
|
||||
httpContext := NewAPITestContext(t, user2.Name, apiRepo.Name, auth_model.AccessTokenScopeWriteRepository)
|
||||
doAPIDeleteRepository(httpContext)(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestActionsJobNeedsMatrix(t *testing.T) {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
t.Skip()
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/structs"
|
||||
|
||||
pingv1 "code.gitea.io/actions-proto-go/ping/v1"
|
||||
"code.gitea.io/actions-proto-go/ping/v1/pingv1connect"
|
||||
|
@ -96,6 +97,61 @@ func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, run
|
|||
r.doRegister(t, runnerName, registrationToken.Token, labels)
|
||||
}
|
||||
|
||||
func (r *mockRunner) registerAsRepoRunnerWithPost(t *testing.T, ownerName, repoName, runnerName string, labels []string) {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
// registering a mock runner when using a database other than SQLite leaves leftovers
|
||||
t.FailNow()
|
||||
}
|
||||
session := loginUser(t, ownerName)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/registration-token", ownerName, repoName)).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var registrationToken struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
DecodeJSON(t, resp, ®istrationToken)
|
||||
r.doRegister(t, runnerName, registrationToken.Token, labels)
|
||||
}
|
||||
|
||||
func (r *mockRunner) listRunners(t *testing.T, ownerName, repoName string) structs.ActionRunnersResponse {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
// registering a mock runner when using a database other than SQLite leaves leftovers
|
||||
t.FailNow()
|
||||
}
|
||||
session := loginUser(t, ownerName)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners", ownerName, repoName)).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var runnersList structs.ActionRunnersResponse
|
||||
DecodeJSON(t, resp, &runnersList)
|
||||
return runnersList
|
||||
}
|
||||
|
||||
func (r *mockRunner) getRunner(t *testing.T, ownerName, repoName string, runnerID int64) structs.ActionRunner {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
// registering a mock runner when using a database other than SQLite leaves leftovers
|
||||
t.FailNow()
|
||||
}
|
||||
session := loginUser(t, ownerName)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/%d", ownerName, repoName, runnerID)).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var runner structs.ActionRunner
|
||||
DecodeJSON(t, resp, &runner)
|
||||
return runner
|
||||
}
|
||||
|
||||
func (r *mockRunner) deleteRunner(t *testing.T, ownerName, repoName string, runnerID int64) {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
// registering a mock runner when using a database other than SQLite leaves leftovers
|
||||
t.FailNow()
|
||||
}
|
||||
session := loginUser(t, ownerName)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||
req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/%d", ownerName, repoName, runnerID)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task {
|
||||
fetchTimeout := 10 * time.Second
|
||||
if len(timeout) > 0 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue