add disable workflow feature (#26413)
As title, that's simmilar with github. ![image](https://github.com/go-gitea/gitea/assets/25342410/9e8b2444-63e0-4e87-80da-730c1e4d09d6) ![image](https://github.com/go-gitea/gitea/assets/25342410/6c3a3345-3ba7-48c9-9acd-3e621632491b) --------- Signed-off-by: a1012112796 <1012112796@qq.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Jason Song <i@wolfogre.com>
This commit is contained in:
parent
253737eb36
commit
19872063a3
10 changed files with 180 additions and 2 deletions
|
@ -391,7 +391,13 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
|
|||
Type: tp,
|
||||
Config: new(IssuesConfig),
|
||||
}
|
||||
} else if tp == unit.TypeActions {
|
||||
return &RepoUnit{
|
||||
Type: tp,
|
||||
Config: new(ActionsConfig),
|
||||
}
|
||||
}
|
||||
|
||||
return &RepoUnit{
|
||||
Type: tp,
|
||||
Config: new(UnitConfig),
|
||||
|
|
|
@ -6,6 +6,7 @@ package repo
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
|
@ -162,6 +163,42 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {
|
|||
return MergeStyleMerge
|
||||
}
|
||||
|
||||
type ActionsConfig struct {
|
||||
DisabledWorkflows []string
|
||||
}
|
||||
|
||||
func (cfg *ActionsConfig) EnableWorkflow(file string) {
|
||||
cfg.DisabledWorkflows = util.SliceRemoveAll(cfg.DisabledWorkflows, file)
|
||||
}
|
||||
|
||||
func (cfg *ActionsConfig) ToString() string {
|
||||
return strings.Join(cfg.DisabledWorkflows, ",")
|
||||
}
|
||||
|
||||
func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool {
|
||||
return util.SliceContains(cfg.DisabledWorkflows, file)
|
||||
}
|
||||
|
||||
func (cfg *ActionsConfig) DisableWorkflow(file string) {
|
||||
for _, workflow := range cfg.DisabledWorkflows {
|
||||
if file == workflow {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file)
|
||||
}
|
||||
|
||||
// FromDB fills up a ActionsConfig from serialized format.
|
||||
func (cfg *ActionsConfig) FromDB(bs []byte) error {
|
||||
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
|
||||
}
|
||||
|
||||
// ToDB exports a ActionsConfig to a serialized format.
|
||||
func (cfg *ActionsConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
// BeforeSet is invoked from XORM before setting the value of a field of this object.
|
||||
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
|
||||
switch colName {
|
||||
|
@ -175,7 +212,9 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
|
|||
r.Config = new(PullRequestsConfig)
|
||||
case unit.TypeIssues:
|
||||
r.Config = new(IssuesConfig)
|
||||
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects, unit.TypePackages, unit.TypeActions:
|
||||
case unit.TypeActions:
|
||||
r.Config = new(ActionsConfig)
|
||||
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects, unit.TypePackages:
|
||||
fallthrough
|
||||
default:
|
||||
r.Config = new(UnitConfig)
|
||||
|
@ -218,6 +257,11 @@ func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
|
|||
return r.Config.(*ExternalTrackerConfig)
|
||||
}
|
||||
|
||||
// ActionsConfig returns config for unit.ActionsConfig
|
||||
func (r *RepoUnit) ActionsConfig() *ActionsConfig {
|
||||
return r.Config.(*ActionsConfig)
|
||||
}
|
||||
|
||||
func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
|
||||
var tmpUnits []*RepoUnit
|
||||
if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {
|
||||
|
|
30
models/repo/repo_unit_test.go
Normal file
30
models/repo/repo_unit_test.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestActionsConfig(t *testing.T) {
|
||||
cfg := &ActionsConfig{}
|
||||
cfg.DisableWorkflow("test1.yaml")
|
||||
assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
|
||||
|
||||
cfg.DisableWorkflow("test1.yaml")
|
||||
assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
|
||||
|
||||
cfg.EnableWorkflow("test1.yaml")
|
||||
assert.EqualValues(t, []string{}, cfg.DisabledWorkflows)
|
||||
|
||||
cfg.EnableWorkflow("test1.yaml")
|
||||
assert.EqualValues(t, []string{}, cfg.DisabledWorkflows)
|
||||
|
||||
cfg.DisableWorkflow("test1.yaml")
|
||||
cfg.DisableWorkflow("test2.yaml")
|
||||
cfg.DisableWorkflow("test3.yaml")
|
||||
assert.EqualValues(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString())
|
||||
}
|
|
@ -3491,6 +3491,11 @@ runs.status_no_select = All status
|
|||
runs.no_results = No results matched.
|
||||
runs.no_runs = The workflow has no runs yet.
|
||||
|
||||
workflow.disable = Disable Workflow
|
||||
workflow.disable_success = Workflow '%s' disabled successfully.
|
||||
workflow.enable = Enable Workflow
|
||||
workflow.enable_success = Workflow '%s' enabled successfully.
|
||||
|
||||
need_approval_desc = Need approval to run workflows for fork pull request.
|
||||
|
||||
variables = Variables
|
||||
|
|
|
@ -137,6 +137,15 @@ func List(ctx *context.Context) {
|
|||
actorID := ctx.FormInt64("actor")
|
||||
status := ctx.FormInt("status")
|
||||
ctx.Data["CurWorkflow"] = workflow
|
||||
|
||||
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
||||
ctx.Data["ActionsConfig"] = actionsConfig
|
||||
|
||||
if len(workflow) > 0 && ctx.Repo.IsAdmin() {
|
||||
ctx.Data["AllowDisableOrEnableWorkflow"] = true
|
||||
ctx.Data["CurWorkflowDisabled"] = actionsConfig.IsWorkflowDisabled(workflow)
|
||||
}
|
||||
|
||||
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
|
||||
// they will be 0 by default, which indicates get all status or actors
|
||||
ctx.Data["CurActor"] = actorID
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
|
@ -572,3 +573,43 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DisableWorkflowFile(ctx *context_module.Context) {
|
||||
disableOrEnableWorkflowFile(ctx, false)
|
||||
}
|
||||
|
||||
func EnableWorkflowFile(ctx *context_module.Context) {
|
||||
disableOrEnableWorkflowFile(ctx, true)
|
||||
}
|
||||
|
||||
func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable 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)
|
||||
} else {
|
||||
cfg.DisableWorkflow(workflow)
|
||||
}
|
||||
|
||||
if err := repo_model.UpdateRepoUnit(cfgUnit); err != nil {
|
||||
ctx.ServerError("UpdateRepoUnit", err)
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1200,6 +1200,8 @@ func registerRoutes(m *web.Route) {
|
|||
|
||||
m.Group("/actions", func() {
|
||||
m.Get("", actions.List)
|
||||
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
|
||||
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
|
||||
|
||||
m.Group("/runs/{run}", func() {
|
||||
m.Combo("").
|
||||
|
|
|
@ -150,7 +150,14 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||
if len(workflows) == 0 {
|
||||
log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID)
|
||||
} else {
|
||||
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
|
||||
|
||||
for _, wf := range workflows {
|
||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
||||
log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName)
|
||||
continue
|
||||
}
|
||||
|
||||
if wf.TriggerEvent != actions_module.GithubEventPullRequestTarget {
|
||||
detectedWorkflows = append(detectedWorkflows, wf)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<div class="page-content repository actions">
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
|
||||
<div class="ui stackable grid">
|
||||
<div class="four wide column">
|
||||
<div class="ui fluid vertical menu">
|
||||
|
@ -13,12 +15,16 @@
|
|||
{{svg "octicon-alert" 16 "text red"}}
|
||||
</span>
|
||||
{{end}}
|
||||
|
||||
{{if $.ActionsConfig.IsWorkflowDisabled .Entry.Name}}
|
||||
<div class="ui red label">{{$.locale.Tr "disabled"}}</div>
|
||||
{{end}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="twelve wide column content">
|
||||
<div class="ui secondary filter stackable menu gt-je">
|
||||
<div class="ui secondary filter menu gt-je gt-df gt-ac">
|
||||
<!-- Actor -->
|
||||
<div class="ui{{if not .Actors}} disabled{{end}} dropdown jump item">
|
||||
<span class="text">{{.locale.Tr "actions.runs.actor"}}</span>
|
||||
|
@ -57,6 +63,17 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .AllowDisableOrEnableWorkflow}}
|
||||
<button class="ui jump dropdown btn interact-bg gt-p-3">
|
||||
{{svg "octicon-kebab-horizontal"}}
|
||||
<div class="menu">
|
||||
<a class="item link-action" data-url="{{$.Link}}/{{if .CurWorkflowDisabled}}enable{{else}}disable{{end}}?workflow={{$.CurWorkflow}}&actor={{.CurActor}}&status={{$.CurStatus}}">
|
||||
{{if .CurWorkflowDisabled}}{{.locale.Tr "actions.workflow.enable"}}{{else}}{{.locale.Tr "actions.workflow.disable"}}{{end}}
|
||||
</a>
|
||||
</div>
|
||||
</button>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "repo/actions/runs_list" .}}
|
||||
</div>
|
||||
|
|
|
@ -653,6 +653,18 @@ a.label,
|
|||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* replace item margin on secondary menu items with gap and remove both the
|
||||
negative margins on the menu as well as margin on the items */
|
||||
.ui.secondary.menu {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
gap: .35714286em;
|
||||
}
|
||||
.ui.secondary.menu .item {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ui.secondary.menu .dropdown.item:hover,
|
||||
.ui.secondary.menu a.item:hover {
|
||||
color: var(--color-text);
|
||||
|
@ -670,6 +682,11 @@ a.label,
|
|||
padding-right: 0.85714286em;
|
||||
}
|
||||
|
||||
/* remove the menu clearfix so that it won't add undesired gaps when using "gap" */
|
||||
.ui.menu::after {
|
||||
content: normal;
|
||||
}
|
||||
|
||||
.ui.menu .dropdown.item .menu {
|
||||
background: var(--color-body);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue