mirror of
https://github.com/go-gitea/gitea
synced 2024-11-21 18:37:04 +01:00
Combined all RequestActions implementation and merge upstream and remove WIP
Signed-off-by: Alex Lau(AvengerMoJo) <avengermojo@gmail.com>
This commit is contained in:
parent
840ad7eefe
commit
9db836b571
19 changed files with 676 additions and 15 deletions
80
models/actions/require_action.go
Normal file
80
models/actions/require_action.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -169,21 +169,34 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionsConfig struct {
|
type ActionsConfig struct {
|
||||||
DisabledWorkflows []string
|
DisabledWorkflows []string
|
||||||
|
EnabledGlobalWorkflows []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *ActionsConfig) EnableWorkflow(file string) {
|
func (cfg *ActionsConfig) EnableWorkflow(file string) {
|
||||||
cfg.DisabledWorkflows = util.SliceRemoveAll(cfg.DisabledWorkflows, file)
|
cfg.DisabledWorkflows = util.SliceRemoveAll(cfg.DisabledWorkflows, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *ActionsConfig) EnableGlobalWorkflow(file string) {
|
||||||
|
cfg.EnabledGlobalWorkflows = append(cfg.EnabledGlobalWorkflows, file)
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *ActionsConfig) ToString() string {
|
func (cfg *ActionsConfig) ToString() string {
|
||||||
return strings.Join(cfg.DisabledWorkflows, ",")
|
return strings.Join(cfg.DisabledWorkflows, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *ActionsConfig) GetGlobalWorkflow() []string {
|
||||||
|
return cfg.EnabledGlobalWorkflows
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool {
|
func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool {
|
||||||
return slices.Contains(cfg.DisabledWorkflows, file)
|
return slices.Contains(cfg.DisabledWorkflows, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *ActionsConfig) IsGlobalWorkflowEnabled(file string) bool {
|
||||||
|
return slices.Contains(cfg.EnabledGlobalWorkflows, file)
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *ActionsConfig) DisableWorkflow(file string) {
|
func (cfg *ActionsConfig) DisableWorkflow(file string) {
|
||||||
for _, workflow := range cfg.DisabledWorkflows {
|
for _, workflow := range cfg.DisabledWorkflows {
|
||||||
if file == workflow {
|
if file == workflow {
|
||||||
|
@ -194,6 +207,10 @@ func (cfg *ActionsConfig) DisableWorkflow(file string) {
|
||||||
cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file)
|
cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *ActionsConfig) DisableGlobalWorkflow(file string) {
|
||||||
|
cfg.EnabledGlobalWorkflows = util.SliceRemoveAll(cfg.EnabledGlobalWorkflows, file)
|
||||||
|
}
|
||||||
|
|
||||||
// FromDB fills up a ActionsConfig from serialized format.
|
// FromDB fills up a ActionsConfig from serialized format.
|
||||||
func (cfg *ActionsConfig) FromDB(bs []byte) error {
|
func (cfg *ActionsConfig) FromDB(bs []byte) error {
|
||||||
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
|
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
|
||||||
|
|
|
@ -95,6 +95,17 @@ func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) {
|
||||||
return events, nil
|
return events, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DetectGlobalWorkflows(
|
||||||
|
gitRepo *git.Repository,
|
||||||
|
commit *git.Commit,
|
||||||
|
triggedEvent webhook_module.HookEventType,
|
||||||
|
payload api.Payloader,
|
||||||
|
detectSchedule bool,
|
||||||
|
entries git.Entries,
|
||||||
|
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
|
||||||
|
return _DetectWorkflows(gitRepo, commit, triggedEvent, payload, detectSchedule, entries)
|
||||||
|
}
|
||||||
|
|
||||||
func DetectWorkflows(
|
func DetectWorkflows(
|
||||||
gitRepo *git.Repository,
|
gitRepo *git.Repository,
|
||||||
commit *git.Commit,
|
commit *git.Commit,
|
||||||
|
@ -106,7 +117,17 @@ func DetectWorkflows(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
return _DetectWorkflows(gitRepo, commit, triggedEvent, payload, detectSchedule, entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DetectWorkflows(
|
||||||
|
gitRepo *git.Repository,
|
||||||
|
commit *git.Commit,
|
||||||
|
triggedEvent webhook_module.HookEventType,
|
||||||
|
payload api.Payloader,
|
||||||
|
detectSchedule bool,
|
||||||
|
entries git.Entries,
|
||||||
|
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
|
||||||
workflows := make([]*DetectedWorkflow, 0, len(entries))
|
workflows := make([]*DetectedWorkflow, 0, len(entries))
|
||||||
schedules := make([]*DetectedWorkflow, 0, len(entries))
|
schedules := make([]*DetectedWorkflow, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
@ -146,12 +167,25 @@ func DetectWorkflows(
|
||||||
return workflows, schedules, nil
|
return workflows, schedules, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DetectScheduledGlobalWorkflows(gitRepo *git.Repository, commit *git.Commit, entries git.Entries) ([]*DetectedWorkflow, error) {
|
||||||
|
return _DetectScheduledWorkflows(gitRepo, commit, entries)
|
||||||
|
}
|
||||||
|
|
||||||
func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) {
|
func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) {
|
||||||
entries, err := ListWorkflows(commit)
|
entries, err := ListWorkflows(commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return _DetectScheduledWorkflows(gitRepo, commit, entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit, entries git.Entries) ([]*DetectedWorkflow, error) {
|
||||||
|
if gitRepo != nil {
|
||||||
|
log.Trace("detect scheduled workflow for gitRepo.Path: %q", gitRepo.Path)
|
||||||
|
}
|
||||||
|
if commit != nil {
|
||||||
|
log.Trace("detect scheduled commit for commit ID: %q", commit.ID)
|
||||||
|
}
|
||||||
wfs := make([]*DetectedWorkflow, 0, len(entries))
|
wfs := make([]*DetectedWorkflow, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
content, err := GetContentFromEntry(entry)
|
content, err := GetContentFromEntry(entry)
|
||||||
|
|
|
@ -3722,6 +3722,27 @@ runs.no_runs = The workflow has no runs yet.
|
||||||
runs.empty_commit_message = (empty commit message)
|
runs.empty_commit_message = (empty commit message)
|
||||||
runs.expire_log_message = Logs have been purged because they were too old.
|
runs.expire_log_message = Logs have been purged because they were too old.
|
||||||
|
|
||||||
|
require_action = Require Action
|
||||||
|
require_action.require_action_manage_panel = Require Action 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 Action 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 = Disable Workflow
|
||||||
workflow.disable_success = Workflow '%s' disabled successfully.
|
workflow.disable_success = Workflow '%s' disabled successfully.
|
||||||
workflow.enable = Enable Workflow
|
workflow.enable = Enable Workflow
|
||||||
|
@ -3733,6 +3754,13 @@ workflow.run_success = Workflow '%s' run successfully.
|
||||||
workflow.from_ref = Use workflow from
|
workflow.from_ref = Use workflow from
|
||||||
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger.
|
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger.
|
||||||
|
|
||||||
|
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.
|
need_approval_desc = Need approval to run workflows for fork pull request.
|
||||||
|
|
||||||
variables = Variables
|
variables = Variables
|
||||||
|
|
12
routers/web/org/setting/require_action.go
Normal file
12
routers/web/org/setting/require_action.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/services/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RedirectToRepoSetting(ctx *context.Context) {
|
||||||
|
ctx.Redirect(ctx.Org.OrgLink + "/settings/actions/require_action")
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -38,6 +39,7 @@ const (
|
||||||
|
|
||||||
type Workflow struct {
|
type Workflow struct {
|
||||||
Entry git.TreeEntry
|
Entry git.TreeEntry
|
||||||
|
Global bool
|
||||||
ErrMsg string
|
ErrMsg string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +73,19 @@ func List(ctx *context.Context) {
|
||||||
|
|
||||||
var workflows []Workflow
|
var workflows []Workflow
|
||||||
var curWorkflow *model.Workflow
|
var curWorkflow *model.Workflow
|
||||||
|
var globalEntries []*git.TreeEntry
|
||||||
|
globalWorkflow, err := db.Find[actions_model.RequireAction](ctx, actions_model.FindRequireActionOptions{
|
||||||
|
OrgID: ctx.Repo.Repository.Owner.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("Global Workflow DB find fail", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
|
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
|
||||||
ctx.ServerError("IsEmpty", err)
|
ctx.ServerError("IsEmpty", err)
|
||||||
return
|
if len(globalWorkflow) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
} else if !empty {
|
} else if !empty {
|
||||||
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -85,6 +97,23 @@ func List(ctx *context.Context) {
|
||||||
ctx.ServerError("ListWorkflows", err)
|
ctx.ServerError("ListWorkflows", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
for _, gEntry := range globalWorkflow {
|
||||||
|
if gEntry.RepoName == ctx.Repo.Repository.Name {
|
||||||
|
log.Trace("Same Repo conflict: %s\n", gEntry.RepoName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gRepo, _ := repo_model.GetRepositoryByName(ctx, gEntry.OrgID, gEntry.RepoName)
|
||||||
|
gGitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, gRepo)
|
||||||
|
// it may be a hack for now..... not sure any better way to do this
|
||||||
|
gCommit, _ := gGitRepo.GetBranchCommit(gRepo.DefaultBranch)
|
||||||
|
gEntries, _ := actions.ListWorkflows(gCommit)
|
||||||
|
for _, entry := range gEntries {
|
||||||
|
if gEntry.WorkflowName == entry.Name() {
|
||||||
|
globalEntries = append(globalEntries, entry)
|
||||||
|
entries = append(entries, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get all runner labels
|
// Get all runner labels
|
||||||
runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
|
runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
|
||||||
|
@ -103,7 +132,14 @@ func List(ctx *context.Context) {
|
||||||
|
|
||||||
workflows = make([]Workflow, 0, len(entries))
|
workflows = make([]Workflow, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
workflow := Workflow{Entry: *entry}
|
var workflowIsGlobal bool
|
||||||
|
workflowIsGlobal = false
|
||||||
|
for i := range globalEntries {
|
||||||
|
if globalEntries[i] == entry {
|
||||||
|
workflowIsGlobal = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workflow := Workflow{Entry: *entry, Global: workflowIsGlobal}
|
||||||
content, err := actions.GetContentFromEntry(entry)
|
content, err := actions.GetContentFromEntry(entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetContentFromEntry", err)
|
ctx.ServerError("GetContentFromEntry", err)
|
||||||
|
@ -165,6 +201,10 @@ func List(ctx *context.Context) {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workflow := ctx.FormString("workflow")
|
||||||
|
isGlobal := false
|
||||||
|
ctx.Data["CurWorkflow"] = workflow
|
||||||
|
|
||||||
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
||||||
ctx.Data["ActionsConfig"] = actionsConfig
|
ctx.Data["ActionsConfig"] = actionsConfig
|
||||||
|
|
||||||
|
@ -205,6 +245,9 @@ func List(ctx *context.Context) {
|
||||||
ctx.Data["Tags"] = tags
|
ctx.Data["Tags"] = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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")
|
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
|
||||||
|
@ -261,6 +304,9 @@ func List(ctx *context.Context) {
|
||||||
pager.AddParamString("workflow", workflowID)
|
pager.AddParamString("workflow", workflowID)
|
||||||
pager.AddParamString("actor", fmt.Sprint(actorID))
|
pager.AddParamString("actor", fmt.Sprint(actorID))
|
||||||
pager.AddParamString("status", fmt.Sprint(status))
|
pager.AddParamString("status", fmt.Sprint(status))
|
||||||
|
if isGlobal {
|
||||||
|
pager.AddParamString("global", fmt.Sprint(isGlobal))
|
||||||
|
}
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
|
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
|
||||||
|
|
||||||
|
|
|
@ -721,7 +721,15 @@ func EnableWorkflowFile(ctx *context_module.Context) {
|
||||||
disableOrEnableWorkflowFile(ctx, true)
|
disableOrEnableWorkflowFile(ctx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
|
func EnableGlobalWorkflowFile(ctx *context_module.Context) {
|
||||||
|
disableOrEnableGlobalWorkflowFile(ctx, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisableGlobalWorkflowFile(ctx *context_module.Context) {
|
||||||
|
disableOrEnableGlobalWorkflowFile(ctx, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableOrEnable(ctx *context_module.Context, isEnable, isglobal bool) {
|
||||||
workflow := ctx.FormString("workflow")
|
workflow := ctx.FormString("workflow")
|
||||||
if len(workflow) == 0 {
|
if len(workflow) == 0 {
|
||||||
ctx.ServerError("workflow", nil)
|
ctx.ServerError("workflow", nil)
|
||||||
|
@ -731,10 +739,18 @@ func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
|
||||||
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
|
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
|
||||||
cfg := cfgUnit.ActionsConfig()
|
cfg := cfgUnit.ActionsConfig()
|
||||||
|
|
||||||
if isEnable {
|
if isglobal {
|
||||||
cfg.EnableWorkflow(workflow)
|
if isEnable {
|
||||||
|
cfg.DisableGlobalWorkflow(workflow)
|
||||||
|
} else {
|
||||||
|
cfg.EnableGlobalWorkflow(workflow)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cfg.DisableWorkflow(workflow)
|
if isEnable {
|
||||||
|
cfg.EnableWorkflow(workflow)
|
||||||
|
} else {
|
||||||
|
cfg.DisableWorkflow(workflow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo_model.UpdateRepoUnit(ctx, cfgUnit); err != nil {
|
if err := repo_model.UpdateRepoUnit(ctx, cfgUnit); err != nil {
|
||||||
|
@ -742,10 +758,18 @@ func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isEnable {
|
if isglobal {
|
||||||
ctx.Flash.Success(ctx.Tr("actions.workflow.enable_success", workflow))
|
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 {
|
} 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),
|
redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(workflow),
|
||||||
|
@ -913,3 +937,11 @@ func Run(ctx *context_module.Context) {
|
||||||
ctx.Flash.Success(ctx.Tr("actions.workflow.run_success", workflowID))
|
ctx.Flash.Success(ctx.Tr("actions.workflow.run_success", workflowID))
|
||||||
ctx.Redirect(redirectURL)
|
ctx.Redirect(redirectURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
|
||||||
|
disableOrEnable(ctx, isEnable, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableOrEnableGlobalWorkflowFile(ctx *context_module.Context, isEnable bool) {
|
||||||
|
disableOrEnable(ctx, isEnable, true)
|
||||||
|
}
|
||||||
|
|
85
routers/web/repo/setting/require_action.go
Normal file
85
routers/web/repo/setting/require_action.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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 (
|
||||||
|
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{
|
||||||
|
OrgID: vCtx.OrgID,
|
||||||
|
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)
|
||||||
|
}
|
82
routers/web/shared/actions/require_action.go
Normal file
82
routers/web/shared/actions/require_action.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
actions_service "code.gitea.io/gitea/services/actions"
|
||||||
|
"code.gitea.io/gitea/services/context"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetRequireActionDeletePost response for deleting a require action workflow
|
||||||
|
func SetRequireActionContext(ctx *context.Context, opts actions_model.FindRequireActionOptions) {
|
||||||
|
requireActions, count, err := db.FindAndCount[actions_model.RequireAction](ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("CountRequireActions", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["RequireActions"] = requireActions
|
||||||
|
ctx.Data["Total"] = count
|
||||||
|
ctx.Data["OrgID"] = ctx.Org.Organization.ID
|
||||||
|
ctx.Data["OrgName"] = ctx.Org.Organization.Name
|
||||||
|
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
|
||||||
|
ctx.Data["Page"] = pager
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all the available enable global workflow in the org's repo
|
||||||
|
func GlobalEnableWorkflow(ctx *context.Context, orgID int64) {
|
||||||
|
var gwfList []actions_model.GlobalWorkflow
|
||||||
|
orgRepos, err := org_model.GetOrgRepositories(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GlobalEnableWorkflows get org repos: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, repo := range orgRepos {
|
||||||
|
err := repo.LoadUnits(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GlobalEnableWorkflows LoadUnits : ", err)
|
||||||
|
}
|
||||||
|
actionsConfig := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
||||||
|
enabledWorkflows := actionsConfig.GetGlobalWorkflow()
|
||||||
|
for _, workflow := range enabledWorkflows {
|
||||||
|
gwf := actions_model.GlobalWorkflow{
|
||||||
|
RepoName: repo.Name,
|
||||||
|
Filename: workflow,
|
||||||
|
}
|
||||||
|
gwfList = append(gwfList, gwf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.Data["GlobalEnableWorkflows"] = gwfList
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRequireAction(ctx *context.Context, orgID int64, redirectURL string) {
|
||||||
|
ctx.Data["OrgID"] = ctx.Org.Organization.ID
|
||||||
|
form := web.GetForm(ctx).(*forms.RequireActionForm)
|
||||||
|
v, err := actions_service.CreateRequireAction(ctx, orgID, form.RepoName, form.WorkflowName)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("CreateRequireAction: %v", err)
|
||||||
|
ctx.JSONError(ctx.Tr("actions.require_action.creation.failed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Flash.Success(ctx.Tr("actions.require_action.creation.success", v.WorkflowName))
|
||||||
|
ctx.JSONRedirect(redirectURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteRequireAction(ctx *context.Context, redirectURL string) {
|
||||||
|
id := ctx.PathParamInt64(":require_action_id")
|
||||||
|
|
||||||
|
if err := actions_service.DeleteRequireActionByID(ctx, id); err != nil {
|
||||||
|
log.Error("Delete RequireAction [%d] failed: %v", id, err)
|
||||||
|
ctx.JSONError(ctx.Tr("actions.require_action.deletion.failed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Flash.Success(ctx.Tr("actions.require_action.deletion.success"))
|
||||||
|
ctx.JSONRedirect(redirectURL)
|
||||||
|
}
|
|
@ -466,6 +466,14 @@ func registerRoutes(m *web.Router) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addSettingsRequireActionRoutes := func() {
|
||||||
|
m.Group("/require_action", func() {
|
||||||
|
m.Get("", repo_setting.RequireAction)
|
||||||
|
m.Post("/add", web.Bind(forms.RequireActionForm{}), repo_setting.RequireActionCreate)
|
||||||
|
m.Post("/{require_action_id}/delete", repo_setting.RequireActionDelete)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: not all routes need go through same middleware.
|
// FIXME: not all routes need go through same middleware.
|
||||||
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
||||||
|
|
||||||
|
@ -648,6 +656,7 @@ func registerRoutes(m *web.Router) {
|
||||||
|
|
||||||
m.Group("/actions", func() {
|
m.Group("/actions", func() {
|
||||||
m.Get("", user_setting.RedirectToDefaultSetting)
|
m.Get("", user_setting.RedirectToDefaultSetting)
|
||||||
|
addSettingsRequireActionRoutes()
|
||||||
addSettingsRunnersRoutes()
|
addSettingsRunnersRoutes()
|
||||||
addSettingsSecretsRoutes()
|
addSettingsSecretsRoutes()
|
||||||
addSettingsVariablesRoutes()
|
addSettingsVariablesRoutes()
|
||||||
|
@ -943,6 +952,7 @@ func registerRoutes(m *web.Router) {
|
||||||
|
|
||||||
m.Group("/actions", func() {
|
m.Group("/actions", func() {
|
||||||
m.Get("", org_setting.RedirectToDefaultSetting)
|
m.Get("", org_setting.RedirectToDefaultSetting)
|
||||||
|
addSettingsRequireActionRoutes()
|
||||||
addSettingsRunnersRoutes()
|
addSettingsRunnersRoutes()
|
||||||
addSettingsSecretsRoutes()
|
addSettingsSecretsRoutes()
|
||||||
addSettingsVariablesRoutes()
|
addSettingsVariablesRoutes()
|
||||||
|
@ -1405,6 +1415,8 @@ func registerRoutes(m *web.Router) {
|
||||||
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
|
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
|
||||||
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
|
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
|
||||||
m.Post("/run", reqRepoAdmin, actions.Run)
|
m.Post("/run", reqRepoAdmin, actions.Run)
|
||||||
|
m.Post("/global_disable", reqRepoAdmin, actions.DisableGlobalWorkflowFile)
|
||||||
|
m.Post("/global_enable", reqRepoAdmin, actions.EnableGlobalWorkflowFile)
|
||||||
|
|
||||||
m.Group("/runs/{run}", func() {
|
m.Group("/runs/{run}", func() {
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
|
|
|
@ -191,14 +191,55 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||||
}
|
}
|
||||||
|
var globalEntries []*git.TreeEntry
|
||||||
|
globalWorkflow, err := db.Find[actions_model.RequireAction](ctx, actions_model.FindRequireActionOptions{
|
||||||
|
OrgID: input.Repo.OwnerID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Global Entries DB find failed: %w", err)
|
||||||
|
}
|
||||||
|
for _, gEntry := range globalWorkflow {
|
||||||
|
if gEntry.RepoName == input.Repo.Name {
|
||||||
|
log.Trace("Same Repo conflict: %s\n", gEntry.RepoName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gRepo, _ := repo_model.GetRepositoryByName(ctx, gEntry.OrgID, gEntry.RepoName)
|
||||||
|
gGitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, gRepo)
|
||||||
|
gCommit, _ := gGitRepo.GetBranchCommit(gRepo.DefaultBranch)
|
||||||
|
gEntries, _ := actions_module.ListWorkflows(gCommit)
|
||||||
|
for _, entry := range gEntries {
|
||||||
|
if gEntry.WorkflowName == entry.Name() {
|
||||||
|
globalEntries = append(globalEntries, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gWorkflows, gSchedules, err := actions_module.DetectGlobalWorkflows(gitRepo, commit,
|
||||||
|
input.Event,
|
||||||
|
input.Payload,
|
||||||
|
shouldDetectSchedules,
|
||||||
|
globalEntries,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Detect Global workflow failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules",
|
log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules, %d global workflows and %d global schedules",
|
||||||
input.Repo.RepoPath(),
|
input.Repo.RepoPath(),
|
||||||
commit.ID,
|
commit.ID,
|
||||||
input.Event,
|
input.Event,
|
||||||
len(workflows),
|
len(workflows),
|
||||||
len(schedules),
|
len(schedules),
|
||||||
|
len(gWorkflows),
|
||||||
|
len(gSchedules),
|
||||||
)
|
)
|
||||||
|
for _, workflow := range gWorkflows {
|
||||||
|
workflows = append(workflows, workflow)
|
||||||
|
log.Trace("gWorkflows: %v\n", workflow)
|
||||||
|
}
|
||||||
|
for _, schedule := range gSchedules {
|
||||||
|
schedules = append(schedules, schedule)
|
||||||
|
log.Trace("gSchedules: %v\n", schedule)
|
||||||
|
}
|
||||||
|
|
||||||
for _, wf := range workflows {
|
for _, wf := range workflows {
|
||||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
||||||
|
|
22
services/actions/require_action.go
Normal file
22
services/actions/require_action.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateRequireAction(ctx context.Context, orgID int64, repoName, workflowName string) (*actions_model.RequireAction, error) {
|
||||||
|
v, err := actions_model.AddRequireAction(ctx, orgID, repoName, workflowName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteRequireActionByID(ctx context.Context, requireActionID int64) error {
|
||||||
|
return actions_model.DeleteRequireAction(ctx, requireActionID)
|
||||||
|
}
|
|
@ -340,6 +340,11 @@ type EditVariableForm struct {
|
||||||
Data string `binding:"Required;MaxSize(65535)"`
|
Data string `binding:"Required;MaxSize(65535)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RequireActionForm struct {
|
||||||
|
RepoName string `binding:"Required;MaxSize(255)"`
|
||||||
|
WorkflowName string `binding:"Required;MaxSize(255)"`
|
||||||
|
}
|
||||||
|
|
||||||
func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
ctx := context.GetValidateContext(req)
|
ctx := context.GetValidateContext(req)
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
{{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings actions")}}
|
{{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings actions")}}
|
||||||
<div class="org-setting-content">
|
<div class="org-setting-content">
|
||||||
{{if eq .PageType "runners"}}
|
{{if eq .PageType "require_action"}}
|
||||||
|
{{template "shared/actions/require_action_list" .}}
|
||||||
|
{{else if eq .PageType "runners"}}
|
||||||
{{template "shared/actions/runner_list" .}}
|
{{template "shared/actions/runner_list" .}}
|
||||||
{{else if eq .PageType "secrets"}}
|
{{else if eq .PageType "secrets"}}
|
||||||
{{template "shared/secrets/add_list" .}}
|
{{template "shared/secrets/add_list" .}}
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
<details class="item toggleable-item" {{if or .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}>
|
<details class="item toggleable-item" {{if or .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}>
|
||||||
<summary>{{ctx.Locale.Tr "actions.actions"}}</summary>
|
<summary>{{ctx.Locale.Tr "actions.actions"}}</summary>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
<a class="{{if .PageIsSharedSettingsRequireAction}}active {{end}}item" href="{{.OrgLink}}/settings/actions/require_action">
|
||||||
|
{{ctx.Locale.Tr "actions.require_action"}}
|
||||||
|
</a>
|
||||||
<a class="{{if .PageIsSharedSettingsRunners}}active {{end}}item" href="{{.OrgLink}}/settings/actions/runners">
|
<a class="{{if .PageIsSharedSettingsRunners}}active {{end}}item" href="{{.OrgLink}}/settings/actions/runners">
|
||||||
{{ctx.Locale.Tr "actions.runners"}}
|
{{ctx.Locale.Tr "actions.runners"}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -10,7 +10,11 @@
|
||||||
<div class="ui fluid vertical menu">
|
<div class="ui fluid vertical menu">
|
||||||
<a class="item{{if not $.CurWorkflow}} active{{end}}" href="?actor={{$.CurActor}}&status={{$.CurStatus}}">{{ctx.Locale.Tr "actions.runs.all_workflows"}}</a>
|
<a class="item{{if not $.CurWorkflow}} active{{end}}" href="?actor={{$.CurActor}}&status={{$.CurStatus}}">{{ctx.Locale.Tr "actions.runs.all_workflows"}}</a>
|
||||||
{{range .workflows}}
|
{{range .workflows}}
|
||||||
<a class="item{{if eq .Entry.Name $.CurWorkflow}} active{{end}}" href="?workflow={{.Entry.Name}}&actor={{$.CurActor}}&status={{$.CurStatus}}">{{.Entry.Name}}
|
{{if .Global}}
|
||||||
|
<a class="item{{if eq .Entry.Name $.CurWorkflow}} active{{end}}" href="?workflow={{.Entry.Name}}&isGlobal=true&actor={{$.CurActor}}&status={{$.CurStatus}}">(global) {{.Entry.Name}}</a>
|
||||||
|
{{else}}
|
||||||
|
<a class="item{{if eq .Entry.Name $.CurWorkflow}} active{{end}}" href="?workflow={{.Entry.Name}}&actor={{$.CurActor}}&status={{$.CurStatus}}">{{.Entry.Name}}</a>
|
||||||
|
{{end}}
|
||||||
{{if .ErrMsg}}
|
{{if .ErrMsg}}
|
||||||
<span data-tooltip-content="{{.ErrMsg}}">
|
<span data-tooltip-content="{{.ErrMsg}}">
|
||||||
{{svg "octicon-alert" 16 "text red"}}
|
{{svg "octicon-alert" 16 "text red"}}
|
||||||
|
@ -20,7 +24,9 @@
|
||||||
{{if $.ActionsConfig.IsWorkflowDisabled .Entry.Name}}
|
{{if $.ActionsConfig.IsWorkflowDisabled .Entry.Name}}
|
||||||
<div class="ui red label">{{ctx.Locale.Tr "disabled"}}</div>
|
<div class="ui red label">{{ctx.Locale.Tr "disabled"}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</a>
|
{{if $.ActionsConfig.IsGlobalWorkflowEnabled .Entry.Name}}
|
||||||
|
<div class="ui red label">{{ctx.Locale.Tr "Global Enabled"}}</div>
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,7 +70,10 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- IsGlobalWorkflowEnabled -->
|
||||||
|
<div class="ui dropdown jump item">
|
||||||
|
<span class="text">{{ctx.Locale.Tr "actions.workflow.global"}}</span>
|
||||||
|
</div>
|
||||||
{{if .AllowDisableOrEnableWorkflow}}
|
{{if .AllowDisableOrEnableWorkflow}}
|
||||||
<button class="ui jump dropdown btn interact-bg tw-p-2">
|
<button class="ui jump dropdown btn interact-bg tw-p-2">
|
||||||
{{svg "octicon-kebab-horizontal"}}
|
{{svg "octicon-kebab-horizontal"}}
|
||||||
|
@ -72,6 +81,10 @@
|
||||||
<a class="item link-action" data-url="{{$.Link}}/{{if .CurWorkflowDisabled}}enable{{else}}disable{{end}}?workflow={{$.CurWorkflow}}&actor={{.CurActor}}&status={{$.CurStatus}}">
|
<a class="item link-action" data-url="{{$.Link}}/{{if .CurWorkflowDisabled}}enable{{else}}disable{{end}}?workflow={{$.CurWorkflow}}&actor={{.CurActor}}&status={{$.CurStatus}}">
|
||||||
{{if .CurWorkflowDisabled}}{{ctx.Locale.Tr "actions.workflow.enable"}}{{else}}{{ctx.Locale.Tr "actions.workflow.disable"}}{{end}}
|
{{if .CurWorkflowDisabled}}{{ctx.Locale.Tr "actions.workflow.enable"}}{{else}}{{ctx.Locale.Tr "actions.workflow.disable"}}{{end}}
|
||||||
</a>
|
</a>
|
||||||
|
<a class="item link-action" data-url="{{$.Link}}/{{if .CurGlobalWorkflowEnable}}global_disable{{else}}global_enable{{end}}?workflow={{$.CurWorkflow}}&actor={{.CurActor}}&status={{$.CurStatus}}">
|
||||||
|
{{if .CurGlobalWorkflowEnable}}{{ctx.Locale.Tr "actions.workflow.global_disable"}}{{else}}{{ctx.Locale.Tr "actions.workflow.global_enable"}}{{end}}
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
126
templates/shared/actions/require_action_list.tmpl
Normal file
126
templates/shared/actions/require_action_list.tmpl
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
<div class="require-actions-container">
|
||||||
|
<h4 class="ui top attached header">
|
||||||
|
{{ctx.Locale.Tr "actions.require_action.require_action_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
|
||||||
|
<div class="ui right">
|
||||||
|
<div class="ui top right">
|
||||||
|
<button class="ui primary tiny button show-modal"
|
||||||
|
data-modal="#add-require-actions-modal"
|
||||||
|
data-modal-form.action="{{.Link}}/add"
|
||||||
|
data-modal-header="{{ctx.Locale.Tr "actions.require_action.add"}}">
|
||||||
|
{{ctx.Locale.Tr "actions.require_action.add"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
<div class="ui attached segment">
|
||||||
|
<form class="ui form ignore-dirty" id="require-action-list-search-form" action="{{$.Link}}">
|
||||||
|
<!-- Search Text -->
|
||||||
|
{{template "shared/search/combo" dict "Value" .Keyword}}
|
||||||
|
<button class="ui primary button">{{ctx.Locale.Tr "actions.require_action.search"}}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="ui attached table segment">
|
||||||
|
<table class="ui very basic striped table unstackable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-sortt-asc="newest" data-sortt-desc="oldest">
|
||||||
|
{{ctx.Locale.Tr "actions.require_action.id"}}
|
||||||
|
</th>
|
||||||
|
<th data-sortt-asc="alphabetically" data-sortt-desc="reversealphabetically">
|
||||||
|
{{ctx.Locale.Tr "actions.require_action.workflow"}}
|
||||||
|
</th>
|
||||||
|
<th>{{ctx.Locale.Tr "actions.require_action.repo"}}</th>
|
||||||
|
<th>{{ctx.Locale.Tr "actions.require_action.link"}}</th>
|
||||||
|
<th>{{ctx.Locale.Tr "actions.require_action.remove"}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{if .RequireActions}}
|
||||||
|
{{range .RequireActions}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.ID}}</td>
|
||||||
|
<td><p data-tooltip-content="{{.RepoName}}">{{.WorkflowName}}</p></td>
|
||||||
|
<td>{{.RepoName}}</td>
|
||||||
|
<td><a href="/{{$.OrgName}}/{{.RepoName}}">Workflow Link</a></td>
|
||||||
|
<td class="require_action-ops">
|
||||||
|
{{if .Removable $.OrgID}}
|
||||||
|
<button class="btn interact-bg tw-p-2 link-action"
|
||||||
|
data-tooltip-content="{{ctx.Locale.Tr "actions.require_action.deletion"}}"
|
||||||
|
data-url="{{$.Link}}/{{.ID}}/delete"
|
||||||
|
data-modal-confirm="{{ctx.Locale.Tr "actions.require_action.deletion.description"}}"
|
||||||
|
>
|
||||||
|
{{svg "octicon-trash"}}
|
||||||
|
</button>
|
||||||
|
<!-- <a href="{{$.Link}}/{{.ID}}/delete">{{svg "octicon-x-circle-fill"}}</a>-->
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td class="center aligned" colspan="8">{{ctx.Locale.Tr "actions.require_action.none"}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{template "base/paginate"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{/* Add RequireAction dialog */}}
|
||||||
|
<div class="ui small modal" id="add-require-actions-modal">
|
||||||
|
<div class="header">
|
||||||
|
<span id="actions-modal-header">Enable Workflows</span>
|
||||||
|
</div>
|
||||||
|
<form class="ui form form-fetch-action" method="post">
|
||||||
|
<div class="content">
|
||||||
|
<div class="item">
|
||||||
|
<a href="https://docs.gitea.com/usage/actions/require-action">{{ctx.Locale.Tr "actions.require_action.enable_global_workflow"}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<table class="ui very basic striped table unstackable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-sortt-asc="alphabetically" data-sortt-desc="reversealphabetically">
|
||||||
|
{{ctx.Locale.Tr "actions.require_action.workflow"}}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ctx.Locale.Tr "actions.require_action.repo"}}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{if .GlobalEnableWorkflows}}
|
||||||
|
{{range .GlobalEnableWorkflows}}
|
||||||
|
<tr>
|
||||||
|
<td><div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input class="select-org-radio" name="workflow_name" type="radio" value="{{.Filename}}">
|
||||||
|
<label>{{.Filename}}</label>
|
||||||
|
</div>
|
||||||
|
<input name="repo_name" type="hidden" value="{{.RepoName}}">
|
||||||
|
</div></td>
|
||||||
|
<td><div class="field">
|
||||||
|
<a href="/{{$.OrgName}}/{{.RepoName}}">
|
||||||
|
<label>{{.RepoName}}</label>
|
||||||
|
</a>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td class="center aligned" colspan="8">{{ctx.Locale.Tr "actions.require_action.none"}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="item">
|
||||||
|
<a href="{{$.Link}}/add">{{ctx.Locale.Tr "actions.require_action.add_require_action"}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
||||||
|
</form>
|
||||||
|
</div>
|
19
web_src/js/features/require-actions-select.js
Normal file
19
web_src/js/features/require-actions-select.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export function initRequireActionsSelect() {
|
||||||
|
const raselect = document.querySelector('add-require-actions-modal');
|
||||||
|
if (!raselect) return;
|
||||||
|
const checkboxes = document.querySelectorAll('.ui.radio.checkbox');
|
||||||
|
for (const box of checkboxes) {
|
||||||
|
box.addEventListener('change', function() {
|
||||||
|
const hiddenInput = this.nextElementSibling;
|
||||||
|
const isChecked = this.querySelector('input[type="radio"]').checked;
|
||||||
|
hiddenInput.disabled = !isChecked;
|
||||||
|
// Disable other hidden inputs
|
||||||
|
for (const otherbox of checkboxes) {
|
||||||
|
const otherHiddenInput = otherbox.nextElementSibling;
|
||||||
|
if (otherbox !== box) {
|
||||||
|
otherHiddenInput.disabled = isChecked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import {initSshKeyFormParser} from './features/sshkey-helper.ts';
|
||||||
import {initUserSettings} from './features/user-settings.ts';
|
import {initUserSettings} from './features/user-settings.ts';
|
||||||
import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts';
|
import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts';
|
||||||
import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts';
|
import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts';
|
||||||
|
import {initRequireActionsSelect} from './features/require-actions-select.js';
|
||||||
import {initRepoDiffView} from './features/repo-diff.ts';
|
import {initRepoDiffView} from './features/repo-diff.ts';
|
||||||
import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.ts';
|
import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.ts';
|
||||||
import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts';
|
import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts';
|
||||||
|
@ -172,6 +173,7 @@ onDomReady(() => {
|
||||||
|
|
||||||
initRepoActivityTopAuthorsChart,
|
initRepoActivityTopAuthorsChart,
|
||||||
initRepoArchiveLinks,
|
initRepoArchiveLinks,
|
||||||
|
initRequireActionsSelect,
|
||||||
initRepoBranchButton,
|
initRepoBranchButton,
|
||||||
initRepoCodeView,
|
initRepoCodeView,
|
||||||
initBranchSelectorTabs,
|
initBranchSelectorTabs,
|
||||||
|
|
Loading…
Reference in a new issue