jojo/routers/web/repo/setting/runners.go
Andreas Ahlenstorf 6ff4147688 refactor: replace WithAvailable with WithVisible when fetching runners (#11657)
When fetching runners, the option `WithAvailable` can be enabled to fetch all runners that can be used by a repository, user, or organization, not only those that are owned by the respective repository, user, or organization. In the instance scope, `WithAvailable` has no meaning. You always get _all_ runners. This means it is impossible to only fetch runners that are owned by the instance, but no others.

This PR replaces `WithAvailable` with `WithVisible`. For repositories, users, and organizations, it has the same semantics as `WithAvailable`. For the instance scope, `WithVisible=true` equals today's default behaviour (i.e., return _all_ runners), whereas `WithVisible=false` is new and would only return the runners owned by the instance itself.

The advantage of `WithVisible` is that it has a consistent meaning across all scopes. This also lays the groundwork for the introduction of a `with-visible` (tentative name) flag in the HTTP API.

## 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 for Go changes

(can be removed for JavaScript changes)

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [x] `make pr-go` before pushing

### Tests for JavaScript changes

(can be removed for Go changes)

- 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.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

*The decision if the pull request will be shown in the release notes is up to the mergers / release team.*

The content of the `release-notes/<pull request number>.md` file will serve as the basis for the release notes. If the file does not exist, the title of the pull request will be used instead.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11657
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
Co-committed-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
2026-03-13 01:43:32 +01:00

261 lines
8.1 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"errors"
"fmt"
"net/url"
actions_model "forgejo.org/models/actions"
"forgejo.org/models/db"
"forgejo.org/modules/base"
"forgejo.org/modules/setting"
actions_shared "forgejo.org/routers/web/shared/actions"
shared_user "forgejo.org/routers/web/shared/user"
"forgejo.org/services/context"
)
const (
tplAdminRunnerCreate base.TplName = "admin/runners/create"
tplAdminRunnerDetails base.TplName = "admin/runners/details"
tplAdminRunnerEdit base.TplName = "admin/runners/edit"
tplAdminRunnerSetup base.TplName = "admin/runners/setup"
tplAdminRunners base.TplName = "admin/actions"
tplOrgRunnerCreate base.TplName = "org/settings/runners_create"
tplOrgRunnerDetails base.TplName = "org/settings/runners_details"
tplOrgRunnerEdit base.TplName = "org/settings/runners_edit"
tplOrgRunnerSetup base.TplName = "org/settings/runners_setup"
tplOrgRunners base.TplName = "org/settings/actions"
tplRepoRunnerCreate base.TplName = "repo/settings/runner_create"
tplRepoRunnerDetails base.TplName = "repo/settings/runner_details"
tplRepoRunnerEdit base.TplName = "repo/settings/runner_edit"
tplRepoRunnerSetup base.TplName = "repo/settings/runner_setup"
tplRepoRunners base.TplName = "repo/settings/actions"
tplUserRunnerCreate base.TplName = "user/settings/runner_create"
tplUserRunnerDetails base.TplName = "user/settings/runner_details"
tplUserRunnerEdit base.TplName = "user/settings/runner_edit"
tplUserRunnerSetup base.TplName = "user/settings/runner_setup"
tplUserRunners base.TplName = "user/settings/actions"
)
type runnersCtx struct {
OwnerID int64
RepoID int64
IsRepo bool
IsOrg bool
IsAdmin bool
IsUser bool
RunnerCreateTemplate base.TplName
RunnerDetailsTemplate base.TplName
RunnerEditTemplate base.TplName
RunnerSetupTemplate base.TplName
RunnersTemplate base.TplName
RedirectLink string
}
func getRunnersCtx(ctx *context.Context) (*runnersCtx, error) {
if ctx.Data["PageIsRepoSettings"] == true {
return &runnersCtx{
RepoID: ctx.Repo.Repository.ID,
OwnerID: 0,
IsRepo: true,
RunnerCreateTemplate: tplRepoRunnerCreate,
RunnerDetailsTemplate: tplRepoRunnerDetails,
RunnerEditTemplate: tplRepoRunnerEdit,
RunnerSetupTemplate: tplRepoRunnerSetup,
RunnersTemplate: tplRepoRunners,
RedirectLink: ctx.Repo.RepoLink + "/settings/actions/runners/",
}, nil
}
if ctx.Data["PageIsOrgSettings"] == true {
err := shared_user.LoadHeaderCount(ctx)
if err != nil {
return nil, fmt.Errorf("could not load project and package counts: %w", err)
}
return &runnersCtx{
RepoID: 0,
OwnerID: ctx.Org.Organization.ID,
IsOrg: true,
RunnerCreateTemplate: tplOrgRunnerCreate,
RunnerDetailsTemplate: tplOrgRunnerDetails,
RunnerEditTemplate: tplOrgRunnerEdit,
RunnerSetupTemplate: tplOrgRunnerSetup,
RunnersTemplate: tplOrgRunners,
RedirectLink: ctx.Org.OrgLink + "/settings/actions/runners/",
}, nil
}
if ctx.Data["PageIsAdmin"] == true {
return &runnersCtx{
RepoID: 0,
OwnerID: 0,
IsAdmin: true,
RunnerCreateTemplate: tplAdminRunnerCreate,
RunnerDetailsTemplate: tplAdminRunnerDetails,
RunnerEditTemplate: tplAdminRunnerEdit,
RunnerSetupTemplate: tplAdminRunnerSetup,
RunnersTemplate: tplAdminRunners,
RedirectLink: setting.AppSubURL + "/admin/actions/runners/",
}, nil
}
if ctx.Data["PageIsUserSettings"] == true {
return &runnersCtx{
OwnerID: ctx.Doer.ID,
RepoID: 0,
IsUser: true,
RunnerCreateTemplate: tplUserRunnerCreate,
RunnerDetailsTemplate: tplUserRunnerDetails,
RunnerEditTemplate: tplUserRunnerEdit,
RunnerSetupTemplate: tplUserRunnerSetup,
RunnersTemplate: tplUserRunners,
RedirectLink: setting.AppSubURL + "/user/settings/actions/runners/",
}, nil
}
return nil, errors.New("unable to set Runners context")
}
// Runners renders the list of all available runners.
func Runners(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
page := ctx.FormInt("page")
if page <= 1 {
page = 1
}
opts := actions_model.FindRunnerOptions{
ListOptions: db.ListOptions{
Page: page,
PageSize: 100,
},
WithVisible: true,
Sort: ctx.Req.URL.Query().Get("sort"),
Filter: ctx.Req.URL.Query().Get("q"),
}
if rCtx.IsRepo {
opts.RepoID = rCtx.RepoID
} else if rCtx.IsOrg || rCtx.IsUser {
opts.OwnerID = rCtx.OwnerID
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
actions_shared.RunnersList(ctx, rCtx.RunnersTemplate, opts)
}
// RunnersDetails renders a read-only view of the most important properties of a runner. It is accessible to every user
// that can use that particular runner.
func RunnersDetails(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
runnerID := ctx.ParamsInt64(":runnerid")
page := ctx.FormInt("page")
if page <= 1 {
page = 1
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
actions_shared.RunnerDetails(ctx, runnerID, rCtx.OwnerID, rCtx.RepoID, rCtx.RunnerDetailsTemplate, page)
}
// RunnersCreate renders the form for creating a new runner.
func RunnersCreate(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
actions_shared.RunnerCreate(ctx, rCtx.RunnerCreateTemplate)
}
// RunnersCreatePost handles the form submitted by RunnersCreate.
func RunnersCreatePost(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
actions_shared.RunnerCreatePost(ctx, rCtx.OwnerID, rCtx.RepoID, rCtx.RunnerCreateTemplate, rCtx.RunnerSetupTemplate)
}
// RunnersEdit renders the form for changing an existing runner.
func RunnersEdit(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
actions_shared.RunnerEdit(ctx, ctx.ParamsInt64(":runnerid"), rCtx.OwnerID, rCtx.RepoID, rCtx.RunnerEditTemplate)
}
// RunnersEditPost handles the form submitted by RunnersEdit.
func RunnersEditPost(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
runnerID := ctx.ParamsInt64(":runnerid")
redirectURL := rCtx.RedirectLink + url.PathEscape(ctx.Params(":runnerid"))
actions_shared.RunnerEditPost(ctx, runnerID, rCtx.OwnerID, rCtx.RepoID, rCtx.RunnerEditTemplate,
rCtx.RunnerSetupTemplate, redirectURL)
}
// ResetRunnerRegistrationToken handles the request to reset the runner registration token.
func ResetRunnerRegistrationToken(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
actions_shared.RunnerResetRegistrationToken(ctx, rCtx.OwnerID, rCtx.RepoID, rCtx.RedirectLink)
}
// RunnerDeletePost handles the request to delete a runner.
func RunnerDeletePost(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
ctx.Data["RunnersListLink"] = rCtx.RedirectLink
runnerID := ctx.ParamsInt64(":runnerid")
successRedirectURL := rCtx.RedirectLink
failureRedirectURL := rCtx.RedirectLink
actions_shared.RunnerDeletePost(ctx, runnerID, rCtx.OwnerID, rCtx.RepoID, successRedirectURL, failureRedirectURL)
}
func RedirectToDefaultSetting(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/runners")
}