Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Wip require action feature fail post #30067

82 changes: 82 additions & 0 deletions models/actions/require_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

// WIP RequireAction

package actions

import (
"context"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"

"xorm.io/builder"
)

type RequireAction struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"index"`
RepoName string `xorm:"VARCHAR(255)"`
WorkflowName string `xorm:"VARCHAR(255) UNIQUE(require_action) NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}

type GlobalWorkflow struct {
RepoName string
Filename string
}

func init() {
db.RegisterModel(new(RequireAction))
}

type FindRequireActionOptions struct {
db.ListOptions
RequireActionID int64
OrgID int64
RepoName string
}

func (opts FindRequireActionOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.OrgID > 0 {
cond = cond.And(builder.Eq{"org_id": opts.OrgID})
}
if opts.RequireActionID > 0 {
cond = cond.And(builder.Eq{"id": opts.RequireActionID})
}
if opts.RepoName != "" {
cond = cond.And(builder.Eq{"repo_name": opts.RepoName})
}
return cond
}

// LoadAttributes loads the attributes of the require action
func (r *RequireAction) LoadAttributes(ctx context.Context) error {
// place holder for now.
return nil
}

// if the workflow is removable
func (r *RequireAction) Removable(orgID int64) bool {
// everyone can remove for now
return r.OrgID == orgID
}

func AddRequireAction(ctx context.Context, orgID int64, repoName, workflowName string) (*RequireAction, error) {
ra := &RequireAction{
OrgID: orgID,
RepoName: repoName,
WorkflowName: workflowName,
}
return ra, db.Insert(ctx, ra)
}

func DeleteRequireAction(ctx context.Context, requireActionID int64) error {
if _, err := db.DeleteByID[RequireAction](ctx, requireActionID); err != nil {
return err
}
return nil
}
3 changes: 3 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,10 @@ var migrations = []Migration{

// Gitea 1.22.0 ends at 294

// v294 -> v295
NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
// v295 -> v296
NewMigration("Add RequireAction Global Workflow", v1_23.AddRequireActionTable),
}

// GetCurrentDBVersion returns the current db version
Expand Down
22 changes: 22 additions & 0 deletions models/migrations/v1_23/v295.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_23 //nolint

import (
"code.gitea.io/gitea/modules/timeutil"

"xorm.io/xorm"
)

func AddRequireActionTable(x *xorm.Engine) error {
type RequireAction struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"index"`
RepoName string `xorm:"VARCHAR(255)"`
WorkflowName string `xorm:"VARCHAR(255) UNIQUE(require_action) NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
return x.Sync(new(RequireAction))
}
19 changes: 18 additions & 1 deletion models/repo/repo_unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,30 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {
}

type ActionsConfig struct {
DisabledWorkflows []string
DisabledWorkflows []string
EnabledGlobalWorkflows []string
}

func (cfg *ActionsConfig) EnableWorkflow(file string) {
cfg.DisabledWorkflows = util.SliceRemoveAll(cfg.DisabledWorkflows, file)
}

func (cfg *ActionsConfig) DisableGlobalWorkflow(file string) {
cfg.EnabledGlobalWorkflows = util.SliceRemoveAll(cfg.EnabledGlobalWorkflows, file)
}

func (cfg *ActionsConfig) IsGlobalWorkflowEnabled(file string) bool {
return slices.Contains(cfg.EnabledGlobalWorkflows, file)
}

func (cfg *ActionsConfig) EnableGlobalWorkflow(file string) {
cfg.EnabledGlobalWorkflows = append(cfg.EnabledGlobalWorkflows, file)
}

func (cfg *ActionsConfig) GetGlobalWorkflow() []string {
return cfg.EnabledGlobalWorkflows
}

func (cfg *ActionsConfig) ToString() string {
return strings.Join(cfg.DisabledWorkflows, ",")
}
Expand Down
27 changes: 27 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3640,11 +3640,38 @@ runs.no_workflows.documentation = For more information on Gitea Actions, see <a
runs.no_runs = The workflow has no runs yet.
runs.empty_commit_message = (empty commit message)

require_action = Require Actions
require_action.require_action_manage_panel = Require Actions Management Panel
require_action.enable_global_workflow = How to Enable Global Workflow
require_action.id = ID
require_action.add = Add Global Workflow
require_action.add_require_action = Enable selected Workflow
require_action.new = Create New
require_action.status = Status
require_action.search = Search...
require_action.version = Version
require_action.repo = Repo Name
require_action.workflow = Workflow Filename
require_action.link = Link
require_action.remove = Remove
require_action.none = No Require Actions Available.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
require_action.none = No Require Actions Available.
require_action.none = No Required Actions Available.

require_action.creation.failed = Create Global Require Action %s Failed.
require_action.creation.success = Create Global Require Action %s successfully.
require_action.deletion = Delete
require_action.deletion.description = Removing the Global Require Action is permanent and cannot be undone. Continue?
require_action.deletion.success = The Global Require Action has been removed.

workflow.disable = Disable Workflow
workflow.disable_success = Workflow '%s' disabled successfully.
workflow.enable = Enable Workflow
workflow.enable_success = Workflow '%s' enabled successfully.
workflow.disabled = Workflow is disabled.
workflow.global = Global
workflow.global_disable = Disable Global Require
workflow.global_disable_success = Global Require '%s' disabled successfully.
workflow.global_enable = Enable Global Require
workflow.global_enable_success = Global Require '%s' enabled successfully.
workflow.global_enabled = Global Require is disabled.

need_approval_desc = Need approval to run workflows for fork pull request.

Expand Down
14 changes: 14 additions & 0 deletions routers/web/org/setting/require_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

// WIP RequireAction

package setting

import (
"code.gitea.io/gitea/services/context"
)

func RedirectToRepoSetting(ctx *context.Context) {
ctx.Redirect(ctx.Org.OrgLink + "/settings/actions/require_action")
}
6 changes: 6 additions & 0 deletions routers/web/repo/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func List(ctx *context.Context) {
workflow := ctx.FormString("workflow")
actorID := ctx.FormInt64("actor")
status := ctx.FormInt("status")
isGlobal := false
ctx.Data["CurWorkflow"] = workflow

actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
Expand All @@ -153,6 +154,8 @@ func List(ctx *context.Context) {
if len(workflow) > 0 && ctx.Repo.IsAdmin() {
ctx.Data["AllowDisableOrEnableWorkflow"] = true
ctx.Data["CurWorkflowDisabled"] = actionsConfig.IsWorkflowDisabled(workflow)
ctx.Data["CurGlobalWorkflowEnable"] = actionsConfig.IsGlobalWorkflowEnabled(workflow)
isGlobal = actionsConfig.IsGlobalWorkflowEnabled(workflow)
}

// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
Expand Down Expand Up @@ -209,6 +212,9 @@ func List(ctx *context.Context) {
pager.AddParamString("workflow", workflow)
pager.AddParamString("actor", fmt.Sprint(actorID))
pager.AddParamString("status", fmt.Sprint(status))
if isGlobal {
pager.AddParamString("global", fmt.Sprint(isGlobal))
}
ctx.Data["Page"] = pager
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0

Expand Down
49 changes: 38 additions & 11 deletions routers/web/repo/actions/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,33 +684,60 @@ func EnableWorkflowFile(ctx *context_module.Context) {
}

func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
disableOrEnable(ctx, isEnable, false)
}

func disableOrEnable(ctx *context_module.Context, isEnable, isglobal bool) {
workflow := ctx.FormString("workflow")
if len(workflow) == 0 {
ctx.ServerError("workflow", nil)
return
}

cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
cfg := cfgUnit.ActionsConfig()

if isEnable {
cfg.EnableWorkflow(workflow)
if isglobal {
if isEnable {
cfg.DisableGlobalWorkflow(workflow)
} else {
cfg.EnableGlobalWorkflow(workflow)
}
} else {
cfg.DisableWorkflow(workflow)
if isEnable {
cfg.EnableWorkflow(workflow)
} else {
cfg.DisableWorkflow(workflow)
}
}

if err := repo_model.UpdateRepoUnit(ctx, cfgUnit); err != nil {
ctx.ServerError("UpdateRepoUnit", err)
return
}

if isEnable {
ctx.Flash.Success(ctx.Tr("actions.workflow.enable_success", workflow))
if isglobal {
if isEnable {
ctx.Flash.Success(ctx.Tr("actions.workflow.global_disable_success", workflow))
} else {
ctx.Flash.Success(ctx.Tr("actions.workflow.global_enable_success", workflow))
}
} else {
ctx.Flash.Success(ctx.Tr("actions.workflow.disable_success", workflow))
if isEnable {
ctx.Flash.Success(ctx.Tr("actions.workflow.enable_success", workflow))
} else {
ctx.Flash.Success(ctx.Tr("actions.workflow.disable_success", workflow))
}
}

redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(workflow),
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))
ctx.JSONRedirect(redirectURL)
}

func DisableGlobalWorkflowFile(ctx *context_module.Context) {
disableOrEnableGlobalWorkflowFile(ctx, true)
}

func EnableGlobalWorkflowFile(ctx *context_module.Context) {
disableOrEnableGlobalWorkflowFile(ctx, false)
}

func disableOrEnableGlobalWorkflowFile(ctx *context_module.Context, isEnable bool) {
disableOrEnable(ctx, isEnable, true)
}
87 changes: 87 additions & 0 deletions routers/web/repo/setting/require_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

// WIP RequireAction

package setting

import (
"errors"
"net/http"

actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
shared "code.gitea.io/gitea/routers/web/shared/actions"
"code.gitea.io/gitea/services/context"
)

const (
// let start with org WIP
tplOrgRequireAction base.TplName = "org/settings/actions"
)

type requireActionsCtx struct {
OrgID int64
IsOrg bool
RequireActionTemplate base.TplName
RedirectLink string
}

func getRequireActionCtx(ctx *context.Context) (*requireActionsCtx, error) {
if ctx.Data["PageIsOrgSettings"] == true {
return &requireActionsCtx{
OrgID: ctx.Org.Organization.ID,
IsOrg: true,
RequireActionTemplate: tplOrgRequireAction,
RedirectLink: ctx.Org.OrgLink + "/settings/actions/require_action",
}, nil
}
return nil, errors.New("unable to set Require Actions context")
}

// Listing all RequireAction
func RequireAction(ctx *context.Context) {
ctx.Data["ActionsTitle"] = ctx.Tr("actions.requires")
ctx.Data["PageType"] = "require_action"
ctx.Data["PageIsSharedSettingsRequireAction"] = true

vCtx, err := getRequireActionCtx(ctx)
if err != nil {
ctx.ServerError("getRequireActionCtx", err)
return
}

page := ctx.FormInt("page")
if page <= 1 {
page = 1
}
opts := actions_model.FindRequireActionOptions{
ListOptions: db.ListOptions{
Page: page,
PageSize: 10,
},
}
shared.SetRequireActionContext(ctx, opts)
ctx.Data["Link"] = vCtx.RedirectLink
shared.GlobalEnableWorkflow(ctx, ctx.Org.Organization.ID)
ctx.HTML(http.StatusOK, vCtx.RequireActionTemplate)
}

func RequireActionCreate(ctx *context.Context) {
vCtx, err := getRequireActionCtx(ctx)
if err != nil {
ctx.ServerError("getRequireActionCtx", err)
return
}
shared.CreateRequireAction(ctx, vCtx.OrgID, vCtx.RedirectLink)
}

func RequireActionDelete(ctx *context.Context) {
vCtx, err := getRequireActionCtx(ctx)
if err != nil {
ctx.ServerError("getRequireActionCtx", err)
return
}
shared.DeleteRequireAction(ctx, vCtx.RedirectLink)
}
Loading