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

Actions support workflow dispatch event #28163

Merged
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
fbadf6f
Actions support workflow dispatch event
pangliang Nov 22, 2023
8eb79f3
fix lint check, /actions/run use isAdmin permission
pangliang Nov 23, 2023
d973fc1
workflow_dispatch support inputs config
pangliang Nov 29, 2023
e18bbcc
Use db.Find instead of writing methods for every object (#28084)
pangliang Nov 29, 2023
d0a70d3
Use Crowdin to translate them
pangliang Dec 4, 2023
2d2f921
Add ref selector for run workflow from
pangliang Dec 5, 2023
660cf18
Follow the behavior of github, only render templates, do not directly…
pangliang Dec 5, 2023
2829a88
Form validate
pangliang Dec 5, 2023
f4a55d1
unnecessary parameters
pangliang Dec 7, 2023
5afedfd
There is no need to readWorkflow again
pangliang Dec 7, 2023
1b7864d
Missed Disabled check.
pangliang Dec 7, 2023
0e67591
Checkbox has its own label
pangliang Dec 7, 2023
300f068
ParseRawOn is not required, use WorkflowDispatchConfig instead
pangliang Dec 7, 2023
a7c0016
fix go.sum
pangliang Dec 7, 2023
36c47f1
hard code event workflow_dispatch for now
pangliang Dec 7, 2023
78d636f
AllowTriggerWorkflowDispatchEvent is unnecessary
pangliang Dec 7, 2023
44615a5
support type "number" form validation
pangliang Dec 7, 2023
c24dede
Returns nil when workflow_dispatch is not defined
pangliang Dec 7, 2023
65b0313
Add WorkflowDispatchPayload for pass Webhook event payload to runner
pangliang Dec 7, 2023
faf3b08
DetectedWorkflow unnecessary
pangliang Dec 7, 2023
1f64b24
only required is enough.
pangliang Dec 11, 2023
5e0a0bc
should omit nil check
pangliang Dec 11, 2023
5ddf23a
Remove unsupported description section of workflow_dispatch from docu…
pangliang Dec 11, 2023
a6db416
Fix workflow dispatch ui
TKaxv-7S Dec 11, 2023
ab84156
update gitea.com/gitea/act to v0.2.59
pangliang Mar 1, 2024
c9aeacc
rebase github/gitea main
pangliang Mar 1, 2024
b3203b4
import fmt
pangliang Mar 1, 2024
5b51dfb
import fmt
pangliang Mar 1, 2024
d1f8cef
Merge branch 'main' into actions_support_workflow_dispatch_event
techknowlogick Mar 2, 2024
e78a88d
lint fail: "undefined: util"
pangliang Mar 3, 2024
82e27b8
Merge remote-tracking branch 'origin/main' into actions_support_workf…
pangliang Mar 3, 2024
9fc0e0d
Wrong indentation type(spaces instead of tabs)
pangliang Mar 3, 2024
4a34fc8
fmt-check fail
pangliang Mar 3, 2024
ce55bd5
Adopted suggested change from @silverwind
pangliang Mar 6, 2024
57653ac
smaller submit button
pangliang Mar 6, 2024
28350dc
Revert "smaller submit button"
pangliang Mar 7, 2024
50694f2
smaller branch dropdown button
pangliang Mar 7, 2024
b261c3c
Merge branch 'main' into actions_support_workflow_dispatch_event
silverwind Mar 7, 2024
0027bc9
Merge remote-tracking branch 'origin/main' into actions_support_workf…
pangliang Mar 8, 2024
85ec450
change branch dropdown text font-size to small
pangliang Mar 8, 2024
1d20af3
GetTagNamesByRepoID error handle as branch
pangliang Mar 9, 2024
7c9eba6
remove project-column-header class
pangliang Mar 9, 2024
c0b87a5
remove spaces before colon
pangliang Mar 9, 2024
b2092d2
some Translate text
pangliang Mar 9, 2024
6d3c634
unnecessary i tag
pangliang Mar 9, 2024
8e70046
font-size use css class
pangliang Mar 9, 2024
8d23757
Merge branch 'main' into actions_support_workflow_dispatch_event
silverwind Mar 25, 2024
8312260
Update templates/repo/actions/workflow_dispatch.tmpl
silverwind Mar 25, 2024
2d882b0
Update templates/repo/actions/workflow_dispatch.tmpl
silverwind Mar 25, 2024
65de06e
fix lint check fail
pangliang Mar 29, 2024
4097052
Merge remote-tracking branch 'origin/main' into actions_support_workf…
pangliang Mar 29, 2024
43e0e3d
Merge remote-tracking branch 'origin/main' into actions_support_workf…
pangliang Apr 7, 2024
eb71452
Merge branch 'main' into actions_support_workflow_dispatch_event
techknowlogick Apr 7, 2024
cf5fe7f
Merge branch 'main' into actions_support_workflow_dispatch_event
silverwind Apr 8, 2024
83fb4b0
Merge remote-tracking branch 'origin/main' into actions_support_workf…
pangliang Apr 17, 2024
ea31b78
Replace `interface{}` with `any`
pangliang Apr 17, 2024
c159998
Merge remote-tracking branch 'origin/main' into actions_support_workf…
silverwind Apr 24, 2024
d2588fd
Many fixes to the branch selector
silverwind Apr 24, 2024
4683d81
extra empty line at the end of a block
pangliang Apr 25, 2024
899abcf
add workflow dispatch
denyskon Aug 14, 2024
91358ac
Merge branch 'workflow-dispatch' into actions_support_workflow_dispat…
denyskon Aug 14, 2024
052acfe
Merge pull request #5 from denyskon/actions_support_workflow_dispatch…
pangliang Aug 16, 2024
283d475
Use a separate WorkflowDispatch structure in the view to keep the inp…
pangliang Aug 16, 2024
20896ed
Fix lint check error
pangliang Aug 16, 2024
ef3a18a
Fix checks-backend error
pangliang Aug 16, 2024
ecb0f41
Fix checks-backend error
pangliang Aug 16, 2024
d682687
Fix checks-backend error
pangliang Aug 16, 2024
ddd8b37
Merge branch 'main' into actions_support_workflow_dispatch_event
GiteaBot Aug 16, 2024
791e790
Update templates/repo/actions/workflow_dispatch.tmpl
lunny Aug 17, 2024
94029b6
Update templates/repo/issue/branch_selector_field.tmpl
lunny Aug 17, 2024
2ea6fa2
Merge branch 'main' into actions_support_workflow_dispatch_event
GiteaBot Aug 17, 2024
b88ae0f
Merge branch 'main' into actions_support_workflow_dispatch_event
GiteaBot Aug 18, 2024
e3e321b
Merge branch 'main' into actions_support_workflow_dispatch_event
GiteaBot Aug 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions docs/content/usage/actions/comparison.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ See [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/usin

Gitea Actions only supports `runs-on: xyz` or `runs-on: [xyz]` now.

### `workflow_dispatch`

See [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onworkflow_dispatch).

It's ignored by Gitea Actions now.

### `hashFiles` expression

See [Expressions](https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles)
Expand Down
6 changes: 0 additions & 6 deletions docs/content/usage/actions/comparison.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ Gitea Actions 目前不支持此功能。

Gitea Actions目前只支持`runs-on: xyz`或`runs-on: [xyz]`。

### `workflow_dispatch`

请参阅[GitHub Actions的工作流语法](https://docs.github.com/zh/actions/using-workflows/workflow-syntax-for-github-actions#onworkflow_dispatch)。

Gitea Actions目前不支持此功能。

### `hashFiles`表达式

请参阅[表达式](https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles)。
Expand Down
14 changes: 14 additions & 0 deletions modules/structs/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,17 @@ type PackagePayload struct {
func (p *PackagePayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}

// WorkflowDispatchPayload represents a workflow dispatch payload
type WorkflowDispatchPayload struct {
Workflow string `json:"workflow"`
Ref string `json:"ref"`
Inputs map[string]interface{} `json:"inputs"`
Repository *Repository `json:"repository"`
Sender *User `json:"sender"`
}

// JSONPayload implements Payload
func (p *WorkflowDispatchPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
6 changes: 6 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ org_still_own_repo = "This organization still owns one or more repositories, del
org_still_own_packages = "This organization still owns one or more packages, delete them first."

target_branch_not_exist = Target branch does not exist.
target_ref_not_exist = Target ref does not exist %s

admin_cannot_delete_self = You cannot delete yourself when you are an admin. Please remove your admin privileges first.

Expand Down Expand Up @@ -3650,6 +3651,11 @@ workflow.disable_success = Workflow '%s' disabled successfully.
workflow.enable = Enable Workflow
workflow.enable_success = Workflow '%s' enabled successfully.
workflow.disabled = Workflow is disabled.
workflow.run = Run Workflow
workflow.not_found = Workflow '%s' not found.
workflow.run_success = Workflow '%s' run successfully.
workflow.from_ref = Use workflow from
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger.

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

Expand Down
59 changes: 50 additions & 9 deletions routers/web/repo/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ import (
"bytes"
"fmt"
"net/http"
"slices"
"strings"

actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
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"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/repo"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
Expand Down Expand Up @@ -58,8 +62,13 @@ func MustEnableActions(ctx *context.Context) {
func List(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("actions.actions")
ctx.Data["PageIsActions"] = true
workflowID := ctx.FormString("workflow")
actorID := ctx.FormInt64("actor")
status := ctx.FormInt("status")
ctx.Data["CurWorkflow"] = workflowID

var workflows []Workflow
var curWorkflow *model.Workflow
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
ctx.ServerError("IsEmpty", err)
return
Expand Down Expand Up @@ -124,6 +133,11 @@ func List(ctx *context.Context) {
}
}
workflows = append(workflows, workflow)

if workflow.Entry.Name() == workflowID {
curWorkflow = wf
}

}
}
ctx.Data["workflows"] = workflows
Expand All @@ -134,17 +148,44 @@ func List(ctx *context.Context) {
page = 1
}

workflow := ctx.FormString("workflow")
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() {
if len(workflowID) > 0 && ctx.Repo.IsAdmin() {
ctx.Data["AllowDisableOrEnableWorkflow"] = true
ctx.Data["CurWorkflowDisabled"] = actionsConfig.IsWorkflowDisabled(workflow)
isWorkflowDisabled := actionsConfig.IsWorkflowDisabled(workflowID)
ctx.Data["CurWorkflowDisabled"] = isWorkflowDisabled

if !isWorkflowDisabled && curWorkflow != nil {
workflowDispatchConfig := curWorkflow.WorkflowDispatchConfig()
if workflowDispatchConfig != nil {
ctx.Data["WorkflowDispatchConfig"] = workflowDispatchConfig

branchOpts := git_model.FindBranchOptions{
RepoID: ctx.Repo.Repository.ID,
IsDeletedBranch: optional.Some(false),
ListOptions: db.ListOptions{
ListAll: true,
},
}
branches, err := git_model.FindBranchNames(ctx, branchOpts)
if err != nil {
ctx.JSON(http.StatusInternalServerError, err)
return
}
// always put default branch on the top if it exists
if slices.Contains(branches, ctx.Repo.Repository.DefaultBranch) {
lunny marked this conversation as resolved.
Show resolved Hide resolved
branches = util.SliceRemoveAll(branches, ctx.Repo.Repository.DefaultBranch)
branches = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...)
}
ctx.Data["Branches"] = branches

tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

err != nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

err == nil means the function returned correctly. Then set tags to ctx.

Copy link
Member

Choose a reason for hiding this comment

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

== nil = optional code in the if
!= nil = error, stop execution in the if

So, if the tags are optional, then == nil is correct.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I can not understand why tags are optional. 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My previous thought was that if there was an error in the get tag, just use the branch and it would not be blocked.
But there is indeed a lack of effective exception logs to troubleshoot problems. It is better to use the same exception handling as the branch. I will update it.

ctx.Data["Tags"] = tags
}
}
}
}

// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
Expand All @@ -161,7 +202,7 @@ func List(ctx *context.Context) {
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
},
RepoID: ctx.Repo.Repository.ID,
WorkflowID: workflow,
WorkflowID: workflowID,
TriggerUserID: actorID,
}

Expand Down Expand Up @@ -198,7 +239,7 @@ func List(ctx *context.Context) {

pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx)
pager.AddParamString("workflow", workflow)
pager.AddParamString("workflow", workflowID)
pager.AddParamString("actor", fmt.Sprint(actorID))
pager.AddParamString("status", fmt.Sprint(status))
ctx.Data["Page"] = pager
Expand Down
169 changes: 169 additions & 0 deletions routers/web/repo/actions/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,26 @@ import (

actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
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"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
actions_service "code.gitea.io/gitea/services/actions"
context_module "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"

"github.com/nektos/act/pkg/jobparser"
"github.com/nektos/act/pkg/model"
"xorm.io/builder"
)

Expand Down Expand Up @@ -698,3 +706,164 @@ func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))
ctx.JSONRedirect(redirectURL)
}

func Run(ctx *context_module.Context) {
redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(ctx.FormString("workflow")),
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))

workflowID := ctx.FormString("workflow")
if len(workflowID) == 0 {
ctx.ServerError("workflow", nil)
return
}

ref := ctx.FormString("ref")
if len(ref) == 0 {
ctx.ServerError("ref", nil)
return
}

// can not rerun job when workflow is disabled
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
cfg := cfgUnit.ActionsConfig()
if cfg.IsWorkflowDisabled(workflowID) {
ctx.Flash.Error(ctx.Tr("actions.workflow.disabled"))
ctx.Redirect(redirectURL)
return
}

// get target commit of run from specified ref
refName := git.RefName(ref)
var runTargetCommit *git.Commit
var err error
if refName.IsTag() {
runTargetCommit, err = ctx.Repo.GitRepo.GetTagCommit(refName.TagName())
} else if refName.IsBranch() {
runTargetCommit, err = ctx.Repo.GitRepo.GetBranchCommit(refName.BranchName())
} else {
ctx.Flash.Error(ctx.Tr("form.git_ref_name_error", ref))
ctx.Redirect(redirectURL)
return
}
if err != nil {
ctx.Flash.Error(ctx.Tr("form.target_ref_not_exist", ref))
ctx.Redirect(redirectURL)
return
}

// get workflow entry from default branch commit
defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return
}
entries, err := actions.ListWorkflows(defaultBranchCommit)
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return
}

// find workflow from commit
var workflows []*jobparser.SingleWorkflow
for _, entry := range entries {
if entry.Name() == workflowID {
content, err := actions.GetContentFromEntry(entry)
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return
}
workflows, err = jobparser.Parse(content)
if err != nil {
ctx.ServerError("workflow", err)
return
}
break
}
}

if len(workflows) == 0 {
ctx.Flash.Error(ctx.Tr("actions.workflow.not_found", workflowID))
ctx.Redirect(redirectURL)
return
}

// get inputs from post
workflow := &model.Workflow{
RawOn: workflows[0].RawOn,
}
inputs := make(map[string]interface{})
if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
for name, config := range workflowDispatch.Inputs {
value := ctx.Req.PostForm.Get(name)
if config.Type == "boolean" {
// https://www.w3.org/TR/html401/interact/forms.html
// https://stackoverflow.com/questions/11424037/do-checkbox-inputs-only-post-data-if-theyre-checked
// Checkboxes (and radio buttons) are on/off switches that may be toggled by the user.
// A switch is "on" when the control element's checked attribute is set.
// When a form is submitted, only "on" checkbox controls can become successful.
inputs[name] = strconv.FormatBool(value == "on")
} else if value != "" {
inputs[name] = value
} else {
inputs[name] = config.Default
}
}
}

// ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
// https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch
workflowDispatchPayload := &api.WorkflowDispatchPayload{
Workflow: workflowID,
Ref: ref,
Repository: convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeNone}),
Inputs: inputs,
Sender: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone),
}
var eventPayload []byte
if eventPayload, err = workflowDispatchPayload.JSONPayload(); err != nil {
ctx.ServerError("JSONPayload", err)
return
}

run := &actions_model.ActionRun{
Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0],
RepoID: ctx.Repo.Repository.ID,
OwnerID: ctx.Repo.Repository.OwnerID,
WorkflowID: workflowID,
TriggerUserID: ctx.Doer.ID,
Ref: ref,
CommitSHA: runTargetCommit.ID.String(),
IsForkPullRequest: false,
Event: "workflow_dispatch",
TriggerEvent: "workflow_dispatch",
EventPayload: string(eventPayload),
Status: actions_model.StatusWaiting,
}

// cancel running jobs of the same workflow
if err := actions_model.CancelRunningJobs(
ctx,
run.RepoID,
run.Ref,
run.WorkflowID,
run.Event,
); err != nil {
log.Error("CancelRunningJobs: %v", err)
}

// Insert the action run and its associated jobs into the database
if err := actions_model.InsertRun(ctx, run, workflows); err != nil {
ctx.ServerError("workflow", err)
return
}

alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID})
if err != nil {
log.Error("FindRunJobs: %v", err)
}
actions_service.CreateCommitStatus(ctx, alljobs...)

ctx.Flash.Success(ctx.Tr("actions.workflow.run_success", workflowID))
ctx.Redirect(redirectURL)
}
1 change: 1 addition & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,7 @@ func registerRoutes(m *web.Route) {
m.Get("", actions.List)
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
m.Post("/run", reqRepoAdmin, actions.Run)

m.Group("/runs/{run}", func() {
m.Combo("").
Expand Down
5 changes: 5 additions & 0 deletions templates/repo/actions/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
</button>
{{end}}
</div>

{{if .WorkflowDispatchConfig}}
{{template "repo/actions/workflow_dispatch" .}}
{{end}}

{{template "repo/actions/runs_list" .}}
</div>
</div>
Expand Down
Loading
Loading