mirror of
https://github.com/go-gitea/gitea
synced 2025-08-05 00:58:19 +00:00
Add workflow_run api + webhook (#33964)
Implements - https://docs.github.com/en/rest/actions/workflow-jobs?apiVersion=2022-11-28#list-jobs-for-a-workflow-run--code-samples - https://docs.github.com/en/rest/actions/workflow-jobs?apiVersion=2022-11-28#get-a-job-for-a-workflow-run--code-samples - https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository - https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run - `/actions/runs` for global + user + org (Gitea only) - `/actions/jobs` for global + user + org + repository (Gitea only) - workflow_run webhook + action trigger - limitations - workflow id is assigned to a string, this may result into problems in strongly typed clients Fixes - workflow_job webhook url to no longer contain the `runs/<run>` part to align with api - workflow instance does now use it's name inside the file instead of filename if set Refactoring - Moved a lot of logic from workflows/workflow_job into a shared module used by both webhook and api TODO - [x] Verify Keda Compatibility - [x] Edit Webhook API bug is resolved Closes https://github.com/go-gitea/gitea/issues/23670 Closes https://github.com/go-gitea/gitea/issues/23796 Closes https://github.com/go-gitea/gitea/issues/24898 Replaces https://github.com/go-gitea/gitea/pull/28047 and is much more complete --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -42,6 +42,10 @@ func notifyWorkflowJobStatusUpdate(ctx context.Context, jobs []*actions_model.Ac
|
||||
_ = job.LoadAttributes(ctx)
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
|
||||
}
|
||||
if len(jobs) > 0 {
|
||||
job := jobs[0]
|
||||
notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +127,7 @@ func CancelAbandonedJobs(ctx context.Context) error {
|
||||
}
|
||||
CreateCommitStatus(ctx, job)
|
||||
if updated {
|
||||
_ = job.LoadAttributes(ctx)
|
||||
NotifyWorkflowRunStatusUpdateWithReload(ctx, job)
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
|
||||
}
|
||||
}
|
||||
|
@@ -33,4 +33,8 @@ type API interface {
|
||||
GetRunner(*context.APIContext)
|
||||
// DeleteRunner delete runner
|
||||
DeleteRunner(*context.APIContext)
|
||||
// ListWorkflowJobs list jobs
|
||||
ListWorkflowJobs(*context.APIContext)
|
||||
// ListWorkflowRuns list runs
|
||||
ListWorkflowRuns(*context.APIContext)
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
|
||||
@@ -78,9 +79,30 @@ func checkJobsOfRun(ctx context.Context, runID int64) error {
|
||||
_ = job.LoadAttributes(ctx)
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
|
||||
}
|
||||
if len(jobs) > 0 {
|
||||
runUpdated := true
|
||||
for _, job := range jobs {
|
||||
if !job.Status.IsDone() {
|
||||
runUpdated = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if runUpdated {
|
||||
NotifyWorkflowRunStatusUpdateWithReload(ctx, jobs[0])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NotifyWorkflowRunStatusUpdateWithReload(ctx context.Context, job *actions_model.ActionRunJob) {
|
||||
job.Run = nil
|
||||
if err := job.LoadAttributes(ctx); err != nil {
|
||||
log.Error("LoadAttributes: %v", err)
|
||||
return
|
||||
}
|
||||
notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
|
||||
}
|
||||
|
||||
type jobStatusResolver struct {
|
||||
statuses map[int64]actions_model.Status
|
||||
needs map[int64][]int64
|
||||
|
@@ -6,13 +6,16 @@ package actions
|
||||
import (
|
||||
"context"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
perm_model "code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -762,3 +765,41 @@ func (n *actionsNotifier) MigrateRepository(ctx context.Context, doer, u *user_m
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).Notify(ctx)
|
||||
}
|
||||
|
||||
func (n *actionsNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
|
||||
ctx = withMethod(ctx, "WorkflowRunStatusUpdate")
|
||||
|
||||
var org *api.Organization
|
||||
if repo.Owner.IsOrganization() {
|
||||
org = convert.ToOrganization(ctx, organization.OrgFromUser(repo.Owner))
|
||||
}
|
||||
|
||||
status := convert.ToWorkflowRunAction(run.Status)
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository: %v", err)
|
||||
return
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
convertedWorkflow, err := convert.GetActionWorkflow(ctx, gitRepo, repo, run.WorkflowID)
|
||||
if err != nil {
|
||||
log.Error("GetActionWorkflow: %v", err)
|
||||
return
|
||||
}
|
||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run)
|
||||
if err != nil {
|
||||
log.Error("ToActionWorkflowRun: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
newNotifyInput(repo, sender, webhook_module.HookEventWorkflowRun).WithPayload(&api.WorkflowRunPayload{
|
||||
Action: status,
|
||||
Workflow: convertedWorkflow,
|
||||
WorkflowRun: convertedRun,
|
||||
Organization: org,
|
||||
Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
|
||||
Sender: convert.ToUser(ctx, sender, nil),
|
||||
}).Notify(ctx)
|
||||
}
|
||||
|
@@ -178,7 +178,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
||||
}
|
||||
|
||||
if skipWorkflows(input, commit) {
|
||||
if skipWorkflows(ctx, input, commit) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
return handleWorkflows(ctx, detectedWorkflows, commit, input, ref.String())
|
||||
}
|
||||
|
||||
func skipWorkflows(input *notifyInput, commit *git.Commit) bool {
|
||||
func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit) bool {
|
||||
// skip workflow runs with a configured skip-ci string in commit message or pr title if the event is push or pull_request(_sync)
|
||||
// https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs
|
||||
skipWorkflowEvents := []webhook_module.HookEventType{
|
||||
@@ -263,6 +263,27 @@ func skipWorkflows(input *notifyInput, commit *git.Commit) bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
if input.Event == webhook_module.HookEventWorkflowRun {
|
||||
wrun, ok := input.Payload.(*api.WorkflowRunPayload)
|
||||
for i := 0; i < 5 && ok && wrun.WorkflowRun != nil; i++ {
|
||||
if wrun.WorkflowRun.Event != "workflow_run" {
|
||||
return false
|
||||
}
|
||||
r, err := actions_model.GetRunByRepoAndID(ctx, input.Repo.ID, wrun.WorkflowRun.ID)
|
||||
if err != nil {
|
||||
log.Error("GetRunByRepoAndID: %v", err)
|
||||
return true
|
||||
}
|
||||
wrun, err = r.GetWorkflowRunEventPayload()
|
||||
if err != nil {
|
||||
log.Error("GetWorkflowRunEventPayload: %v", err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
// skip workflow runs events exceeding the maxiumum of 5 recursive events
|
||||
log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.RepoPath())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -372,6 +393,15 @@ func handleWorkflows(
|
||||
continue
|
||||
}
|
||||
CreateCommitStatus(ctx, alljobs...)
|
||||
if len(alljobs) > 0 {
|
||||
job := alljobs[0]
|
||||
err := job.LoadRun(ctx)
|
||||
if err != nil {
|
||||
log.Error("LoadRun: %v", err)
|
||||
continue
|
||||
}
|
||||
notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
|
||||
}
|
||||
for _, job := range alljobs {
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, input.Repo, input.Doer, job, nil)
|
||||
}
|
||||
|
@@ -157,6 +157,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
|
||||
if err != nil {
|
||||
log.Error("LoadAttributes: %v", err)
|
||||
}
|
||||
notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run)
|
||||
for _, job := range allJobs {
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, run.Repo, run.TriggerUser, job, nil)
|
||||
}
|
||||
|
@@ -5,9 +5,6 @@ package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
@@ -31,51 +28,8 @@ import (
|
||||
"github.com/nektos/act/pkg/model"
|
||||
)
|
||||
|
||||
func getActionWorkflowEntry(ctx *context.APIContext, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
|
||||
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
|
||||
cfg := cfgUnit.ActionsConfig()
|
||||
|
||||
defaultBranch, _ := commit.GetBranchName()
|
||||
|
||||
workflowURL := fmt.Sprintf("%s/actions/workflows/%s", ctx.Repo.Repository.APIURL(), url.PathEscape(entry.Name()))
|
||||
workflowRepoURL := fmt.Sprintf("%s/src/branch/%s/%s/%s", ctx.Repo.Repository.HTMLURL(ctx), util.PathEscapeSegments(defaultBranch), util.PathEscapeSegments(folder), url.PathEscape(entry.Name()))
|
||||
badgeURL := fmt.Sprintf("%s/actions/workflows/%s/badge.svg?branch=%s", ctx.Repo.Repository.HTMLURL(ctx), url.PathEscape(entry.Name()), url.QueryEscape(ctx.Repo.Repository.DefaultBranch))
|
||||
|
||||
// See https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow
|
||||
// State types:
|
||||
// - active
|
||||
// - deleted
|
||||
// - disabled_fork
|
||||
// - disabled_inactivity
|
||||
// - disabled_manually
|
||||
state := "active"
|
||||
if cfg.IsWorkflowDisabled(entry.Name()) {
|
||||
state = "disabled_manually"
|
||||
}
|
||||
|
||||
// The CreatedAt and UpdatedAt fields currently reflect the timestamp of the latest commit, which can later be refined
|
||||
// by retrieving the first and last commits for the file history. The first commit would indicate the creation date,
|
||||
// while the last commit would represent the modification date. The DeletedAt could be determined by identifying
|
||||
// the last commit where the file existed. However, this implementation has not been done here yet, as it would likely
|
||||
// cause a significant performance degradation.
|
||||
createdAt := commit.Author.When
|
||||
updatedAt := commit.Author.When
|
||||
|
||||
return &api.ActionWorkflow{
|
||||
ID: entry.Name(),
|
||||
Name: entry.Name(),
|
||||
Path: path.Join(folder, entry.Name()),
|
||||
State: state,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
URL: workflowURL,
|
||||
HTMLURL: workflowRepoURL,
|
||||
BadgeURL: badgeURL,
|
||||
}
|
||||
}
|
||||
|
||||
func EnableOrDisableWorkflow(ctx *context.APIContext, workflowID string, isEnable bool) error {
|
||||
workflow, err := GetActionWorkflow(ctx, workflowID)
|
||||
workflow, err := convert.GetActionWorkflow(ctx, ctx.Repo.GitRepo, ctx.Repo.Repository, workflowID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,42 +46,6 @@ func EnableOrDisableWorkflow(ctx *context.APIContext, workflowID string, isEnabl
|
||||
return repo_model.UpdateRepoUnit(ctx, cfgUnit)
|
||||
}
|
||||
|
||||
func ListActionWorkflows(ctx *context.APIContext) ([]*api.ActionWorkflow, error) {
|
||||
defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder, entries, err := actions.ListWorkflows(defaultBranchCommit)
|
||||
if err != nil {
|
||||
ctx.APIError(http.StatusNotFound, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workflows := make([]*api.ActionWorkflow, len(entries))
|
||||
for i, entry := range entries {
|
||||
workflows[i] = getActionWorkflowEntry(ctx, defaultBranchCommit, folder, entry)
|
||||
}
|
||||
|
||||
return workflows, nil
|
||||
}
|
||||
|
||||
func GetActionWorkflow(ctx *context.APIContext, workflowID string) (*api.ActionWorkflow, error) {
|
||||
entries, err := ListActionWorkflows(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.Name == workflowID {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, util.NewNotExistErrorf("workflow %q not found", workflowID)
|
||||
}
|
||||
|
||||
func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, workflowID, ref string, processInputs func(model *model.WorkflowDispatch, inputs map[string]any) error) error {
|
||||
if workflowID == "" {
|
||||
return util.ErrorWrapLocale(
|
||||
@@ -285,6 +203,15 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
|
||||
log.Error("FindRunJobs: %v", err)
|
||||
}
|
||||
CreateCommitStatus(ctx, allJobs...)
|
||||
if len(allJobs) > 0 {
|
||||
job := allJobs[0]
|
||||
err := job.LoadRun(ctx)
|
||||
if err != nil {
|
||||
log.Error("LoadRun: %v", err)
|
||||
} else {
|
||||
notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
|
||||
}
|
||||
}
|
||||
for _, job := range allJobs {
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, repo, doer, job, nil)
|
||||
}
|
||||
|
@@ -12,6 +12,8 @@ import (
|
||||
)
|
||||
|
||||
// FormString returns the first value matching the provided key in the form as a string
|
||||
// It works the same as http.Request.FormValue:
|
||||
// try urlencoded request body first, then query string, then multipart form body
|
||||
func (b *Base) FormString(key string, def ...string) string {
|
||||
s := b.Req.FormValue(key)
|
||||
if s == "" {
|
||||
@@ -20,7 +22,7 @@ func (b *Base) FormString(key string, def ...string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// FormStrings returns a string slice for the provided key from the form
|
||||
// FormStrings returns a values for the key in the form (including query parameters), similar to FormString
|
||||
func (b *Base) FormStrings(key string) []string {
|
||||
if b.Req.Form == nil {
|
||||
if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
|
||||
|
@@ -5,8 +5,11 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -14,6 +17,7 @@ import (
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
@@ -22,6 +26,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
@@ -32,6 +37,7 @@ import (
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
|
||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
)
|
||||
|
||||
// ToEmail convert models.EmailAddress to api.Email
|
||||
@@ -241,6 +247,242 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun) (*api.ActionWorkflowRun, error) {
|
||||
err := run.LoadAttributes(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, conclusion := ToActionsStatus(run.Status)
|
||||
return &api.ActionWorkflowRun{
|
||||
ID: run.ID,
|
||||
URL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), run.ID),
|
||||
HTMLURL: run.HTMLURL(),
|
||||
RunNumber: run.Index,
|
||||
StartedAt: run.Started.AsLocalTime(),
|
||||
CompletedAt: run.Stopped.AsLocalTime(),
|
||||
Event: string(run.Event),
|
||||
DisplayTitle: run.Title,
|
||||
HeadBranch: git.RefName(run.Ref).BranchName(),
|
||||
HeadSha: run.CommitSHA,
|
||||
Status: status,
|
||||
Conclusion: conclusion,
|
||||
Path: fmt.Sprintf("%s@%s", run.WorkflowID, run.Ref),
|
||||
Repository: ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
|
||||
TriggerActor: ToUser(ctx, run.TriggerUser, nil),
|
||||
// We do not have a way to get a different User for the actor than the trigger user
|
||||
Actor: ToUser(ctx, run.TriggerUser, nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ToWorkflowRunAction(status actions_model.Status) string {
|
||||
var action string
|
||||
switch status {
|
||||
case actions_model.StatusWaiting, actions_model.StatusBlocked:
|
||||
action = "requested"
|
||||
case actions_model.StatusRunning:
|
||||
action = "in_progress"
|
||||
}
|
||||
if status.IsDone() {
|
||||
action = "completed"
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func ToActionsStatus(status actions_model.Status) (string, string) {
|
||||
var action string
|
||||
var conclusion string
|
||||
switch status {
|
||||
// This is a naming conflict of the webhook between Gitea and GitHub Actions
|
||||
case actions_model.StatusWaiting:
|
||||
action = "queued"
|
||||
case actions_model.StatusBlocked:
|
||||
action = "waiting"
|
||||
case actions_model.StatusRunning:
|
||||
action = "in_progress"
|
||||
}
|
||||
if status.IsDone() {
|
||||
action = "completed"
|
||||
switch status {
|
||||
case actions_model.StatusSuccess:
|
||||
conclusion = "success"
|
||||
case actions_model.StatusCancelled:
|
||||
conclusion = "cancelled"
|
||||
case actions_model.StatusFailure:
|
||||
conclusion = "failure"
|
||||
case actions_model.StatusSkipped:
|
||||
conclusion = "skipped"
|
||||
}
|
||||
}
|
||||
return action, conclusion
|
||||
}
|
||||
|
||||
// ToActionWorkflowJob convert a actions_model.ActionRunJob to an api.ActionWorkflowJob
|
||||
// task is optional and can be nil
|
||||
func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task *actions_model.ActionTask, job *actions_model.ActionRunJob) (*api.ActionWorkflowJob, error) {
|
||||
err := job.LoadAttributes(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jobIndex := 0
|
||||
jobs, err := actions_model.GetRunJobsByRunID(ctx, job.RunID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, j := range jobs {
|
||||
if j.ID == job.ID {
|
||||
jobIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
status, conclusion := ToActionsStatus(job.Status)
|
||||
var runnerID int64
|
||||
var runnerName string
|
||||
var steps []*api.ActionWorkflowStep
|
||||
|
||||
if job.TaskID != 0 {
|
||||
if task == nil {
|
||||
task, _, err = db.GetByID[actions_model.ActionTask](ctx, job.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
runnerID = task.RunnerID
|
||||
if runner, ok, _ := db.GetByID[actions_model.ActionRunner](ctx, runnerID); ok {
|
||||
runnerName = runner.Name
|
||||
}
|
||||
for i, step := range task.Steps {
|
||||
stepStatus, stepConclusion := ToActionsStatus(job.Status)
|
||||
steps = append(steps, &api.ActionWorkflowStep{
|
||||
Name: step.Name,
|
||||
Number: int64(i),
|
||||
Status: stepStatus,
|
||||
Conclusion: stepConclusion,
|
||||
StartedAt: step.Started.AsTime().UTC(),
|
||||
CompletedAt: step.Stopped.AsTime().UTC(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &api.ActionWorkflowJob{
|
||||
ID: job.ID,
|
||||
// missing api endpoint for this location
|
||||
URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(), job.ID),
|
||||
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), jobIndex),
|
||||
RunID: job.RunID,
|
||||
// Missing api endpoint for this location, artifacts are available under a nested url
|
||||
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID),
|
||||
Name: job.Name,
|
||||
Labels: job.RunsOn,
|
||||
RunAttempt: job.Attempt,
|
||||
HeadSha: job.Run.CommitSHA,
|
||||
HeadBranch: git.RefName(job.Run.Ref).BranchName(),
|
||||
Status: status,
|
||||
Conclusion: conclusion,
|
||||
RunnerID: runnerID,
|
||||
RunnerName: runnerName,
|
||||
Steps: steps,
|
||||
CreatedAt: job.Created.AsTime().UTC(),
|
||||
StartedAt: job.Started.AsTime().UTC(),
|
||||
CompletedAt: job.Stopped.AsTime().UTC(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
|
||||
cfgUnit := repo.MustGetUnit(ctx, unit.TypeActions)
|
||||
cfg := cfgUnit.ActionsConfig()
|
||||
|
||||
defaultBranch, _ := commit.GetBranchName()
|
||||
|
||||
workflowURL := fmt.Sprintf("%s/actions/workflows/%s", repo.APIURL(), util.PathEscapeSegments(entry.Name()))
|
||||
workflowRepoURL := fmt.Sprintf("%s/src/branch/%s/%s/%s", repo.HTMLURL(ctx), util.PathEscapeSegments(defaultBranch), util.PathEscapeSegments(folder), util.PathEscapeSegments(entry.Name()))
|
||||
badgeURL := fmt.Sprintf("%s/actions/workflows/%s/badge.svg?branch=%s", repo.HTMLURL(ctx), util.PathEscapeSegments(entry.Name()), url.QueryEscape(repo.DefaultBranch))
|
||||
|
||||
// See https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow
|
||||
// State types:
|
||||
// - active
|
||||
// - deleted
|
||||
// - disabled_fork
|
||||
// - disabled_inactivity
|
||||
// - disabled_manually
|
||||
state := "active"
|
||||
if cfg.IsWorkflowDisabled(entry.Name()) {
|
||||
state = "disabled_manually"
|
||||
}
|
||||
|
||||
// The CreatedAt and UpdatedAt fields currently reflect the timestamp of the latest commit, which can later be refined
|
||||
// by retrieving the first and last commits for the file history. The first commit would indicate the creation date,
|
||||
// while the last commit would represent the modification date. The DeletedAt could be determined by identifying
|
||||
// the last commit where the file existed. However, this implementation has not been done here yet, as it would likely
|
||||
// cause a significant performance degradation.
|
||||
createdAt := commit.Author.When
|
||||
updatedAt := commit.Author.When
|
||||
|
||||
content, err := actions.GetContentFromEntry(entry)
|
||||
name := entry.Name()
|
||||
if err == nil {
|
||||
workflow, err := model.ReadWorkflow(bytes.NewReader(content))
|
||||
if err == nil {
|
||||
// Only use the name when specified in the workflow file
|
||||
if workflow.Name != "" {
|
||||
name = workflow.Name
|
||||
}
|
||||
} else {
|
||||
log.Error("getActionWorkflowEntry: Failed to parse workflow: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Error("getActionWorkflowEntry: Failed to get content from entry: %v", err)
|
||||
}
|
||||
|
||||
return &api.ActionWorkflow{
|
||||
ID: entry.Name(),
|
||||
Name: name,
|
||||
Path: path.Join(folder, entry.Name()),
|
||||
State: state,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
URL: workflowURL,
|
||||
HTMLURL: workflowRepoURL,
|
||||
BadgeURL: badgeURL,
|
||||
}
|
||||
}
|
||||
|
||||
func ListActionWorkflows(ctx context.Context, gitrepo *git.Repository, repo *repo_model.Repository) ([]*api.ActionWorkflow, error) {
|
||||
defaultBranchCommit, err := gitrepo.GetBranchCommit(repo.DefaultBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder, entries, err := actions.ListWorkflows(defaultBranchCommit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workflows := make([]*api.ActionWorkflow, len(entries))
|
||||
for i, entry := range entries {
|
||||
workflows[i] = getActionWorkflowEntry(ctx, repo, defaultBranchCommit, folder, entry)
|
||||
}
|
||||
|
||||
return workflows, nil
|
||||
}
|
||||
|
||||
func GetActionWorkflow(ctx context.Context, gitrepo *git.Repository, repo *repo_model.Repository, workflowID string) (*api.ActionWorkflow, error) {
|
||||
entries, err := ListActionWorkflows(ctx, gitrepo, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.ID == workflowID {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, util.NewNotExistErrorf("workflow %q not found", workflowID)
|
||||
}
|
||||
|
||||
// ToActionArtifact convert a actions_model.ActionArtifact to an api.ActionArtifact
|
||||
func ToActionArtifact(repo *repo_model.Repository, art *actions_model.ActionArtifact) (*api.ActionArtifact, error) {
|
||||
url := fmt.Sprintf("%s/actions/artifacts/%d", repo.APIURL(), art.ID)
|
||||
|
@@ -234,6 +234,7 @@ type WebhookForm struct {
|
||||
Release bool
|
||||
Package bool
|
||||
Status bool
|
||||
WorkflowRun bool
|
||||
WorkflowJob bool
|
||||
Active bool
|
||||
BranchFilter string `binding:"GlobPattern"`
|
||||
|
@@ -79,5 +79,7 @@ type Notifier interface {
|
||||
|
||||
CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit *repository.PushCommit, sender *user_model.User, status *git_model.CommitStatus)
|
||||
|
||||
WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun)
|
||||
|
||||
WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask)
|
||||
}
|
||||
|
@@ -376,6 +376,12 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit
|
||||
}
|
||||
}
|
||||
|
||||
func WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.WorkflowRunStatusUpdate(ctx, repo, sender, run)
|
||||
}
|
||||
}
|
||||
|
||||
func WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.WorkflowJobStatusUpdate(ctx, repo, sender, job, task)
|
||||
|
@@ -214,5 +214,8 @@ func (*NullNotifier) ChangeDefaultBranch(ctx context.Context, repo *repo_model.R
|
||||
func (*NullNotifier) CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit *repository.PushCommit, sender *user_model.User, status *git_model.CommitStatus) {
|
||||
}
|
||||
|
||||
func (*NullNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
|
||||
}
|
||||
|
||||
func (*NullNotifier) WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask) {
|
||||
}
|
||||
|
@@ -176,6 +176,12 @@ func (dc dingtalkConvertor) Status(p *api.CommitStatusPayload) (DingtalkPayload,
|
||||
return createDingtalkPayload(text, text, "Status Changed", p.TargetURL), nil
|
||||
}
|
||||
|
||||
func (dingtalkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (DingtalkPayload, error) {
|
||||
text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return createDingtalkPayload(text, text, "Workflow Run", p.WorkflowRun.HTMLURL), nil
|
||||
}
|
||||
|
||||
func (dingtalkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (DingtalkPayload, error) {
|
||||
text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
|
@@ -278,6 +278,12 @@ func (d discordConvertor) Status(p *api.CommitStatusPayload) (DiscordPayload, er
|
||||
return d.createPayload(p.Sender, text, "", p.TargetURL, color), nil
|
||||
}
|
||||
|
||||
func (d discordConvertor) WorkflowRun(p *api.WorkflowRunPayload) (DiscordPayload, error) {
|
||||
text, color := getWorkflowRunPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return d.createPayload(p.Sender, text, "", p.WorkflowRun.HTMLURL, color), nil
|
||||
}
|
||||
|
||||
func (d discordConvertor) WorkflowJob(p *api.WorkflowJobPayload) (DiscordPayload, error) {
|
||||
text, color := getWorkflowJobPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
|
@@ -172,6 +172,12 @@ func (fc feishuConvertor) Status(p *api.CommitStatusPayload) (FeishuPayload, err
|
||||
return newFeishuTextPayload(text), nil
|
||||
}
|
||||
|
||||
func (feishuConvertor) WorkflowRun(p *api.WorkflowRunPayload) (FeishuPayload, error) {
|
||||
text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return newFeishuTextPayload(text), nil
|
||||
}
|
||||
|
||||
func (feishuConvertor) WorkflowJob(p *api.WorkflowJobPayload) (FeishuPayload, error) {
|
||||
text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
|
@@ -327,6 +327,37 @@ func getStatusPayloadInfo(p *api.CommitStatusPayload, linkFormatter linkFormatte
|
||||
return text, color
|
||||
}
|
||||
|
||||
func getWorkflowRunPayloadInfo(p *api.WorkflowRunPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
|
||||
description := p.WorkflowRun.Conclusion
|
||||
if description == "" {
|
||||
description = p.WorkflowRun.Status
|
||||
}
|
||||
refLink := linkFormatter(p.WorkflowRun.HTMLURL, fmt.Sprintf("%s(#%d)", p.WorkflowRun.DisplayTitle, p.WorkflowRun.ID)+"["+base.ShortSha(p.WorkflowRun.HeadSha)+"]:"+description)
|
||||
|
||||
text = fmt.Sprintf("Workflow Run %s: %s", p.Action, refLink)
|
||||
switch description {
|
||||
case "waiting":
|
||||
color = orangeColor
|
||||
case "queued":
|
||||
color = orangeColorLight
|
||||
case "success":
|
||||
color = greenColor
|
||||
case "failure":
|
||||
color = redColor
|
||||
case "cancelled":
|
||||
color = yellowColor
|
||||
case "skipped":
|
||||
color = purpleColor
|
||||
default:
|
||||
color = greyColor
|
||||
}
|
||||
if withSender {
|
||||
text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
|
||||
}
|
||||
|
||||
return text, color
|
||||
}
|
||||
|
||||
func getWorkflowJobPayloadInfo(p *api.WorkflowJobPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
|
||||
description := p.WorkflowJob.Conclusion
|
||||
if description == "" {
|
||||
|
@@ -252,6 +252,12 @@ func (m matrixConvertor) Status(p *api.CommitStatusPayload) (MatrixPayload, erro
|
||||
return m.newPayload(text)
|
||||
}
|
||||
|
||||
func (m matrixConvertor) WorkflowRun(p *api.WorkflowRunPayload) (MatrixPayload, error) {
|
||||
text, _ := getWorkflowRunPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return m.newPayload(text)
|
||||
}
|
||||
|
||||
func (m matrixConvertor) WorkflowJob(p *api.WorkflowJobPayload) (MatrixPayload, error) {
|
||||
text, _ := getWorkflowJobPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
|
@@ -318,6 +318,20 @@ func (m msteamsConvertor) Status(p *api.CommitStatusPayload) (MSTeamsPayload, er
|
||||
), nil
|
||||
}
|
||||
|
||||
func (msteamsConvertor) WorkflowRun(p *api.WorkflowRunPayload) (MSTeamsPayload, error) {
|
||||
title, color := getWorkflowRunPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return createMSTeamsPayload(
|
||||
p.Repo,
|
||||
p.Sender,
|
||||
title,
|
||||
"",
|
||||
p.WorkflowRun.HTMLURL,
|
||||
color,
|
||||
&MSTeamsFact{"WorkflowRun:", p.WorkflowRun.DisplayTitle},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (msteamsConvertor) WorkflowJob(p *api.WorkflowJobPayload) (MSTeamsPayload, error) {
|
||||
title, color := getWorkflowJobPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
|
@@ -5,10 +5,8 @@ package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
@@ -18,6 +16,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
@@ -956,72 +955,17 @@ func (*webhookNotifier) WorkflowJobStatusUpdate(ctx context.Context, repo *repo_
|
||||
org = convert.ToOrganization(ctx, organization.OrgFromUser(repo.Owner))
|
||||
}
|
||||
|
||||
err := job.LoadAttributes(ctx)
|
||||
status, _ := convert.ToActionsStatus(job.Status)
|
||||
|
||||
convertedJob, err := convert.ToActionWorkflowJob(ctx, repo, task, job)
|
||||
if err != nil {
|
||||
log.Error("Error loading job attributes: %v", err)
|
||||
log.Error("ToActionWorkflowJob: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
jobIndex := 0
|
||||
jobs, err := actions_model.GetRunJobsByRunID(ctx, job.RunID)
|
||||
if err != nil {
|
||||
log.Error("Error loading getting run jobs: %v", err)
|
||||
return
|
||||
}
|
||||
for i, j := range jobs {
|
||||
if j.ID == job.ID {
|
||||
jobIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
status, conclusion := toActionStatus(job.Status)
|
||||
var runnerID int64
|
||||
var runnerName string
|
||||
var steps []*api.ActionWorkflowStep
|
||||
|
||||
if task != nil {
|
||||
runnerID = task.RunnerID
|
||||
if runner, ok, _ := db.GetByID[actions_model.ActionRunner](ctx, runnerID); ok {
|
||||
runnerName = runner.Name
|
||||
}
|
||||
for i, step := range task.Steps {
|
||||
stepStatus, stepConclusion := toActionStatus(job.Status)
|
||||
steps = append(steps, &api.ActionWorkflowStep{
|
||||
Name: step.Name,
|
||||
Number: int64(i),
|
||||
Status: stepStatus,
|
||||
Conclusion: stepConclusion,
|
||||
StartedAt: step.Started.AsTime().UTC(),
|
||||
CompletedAt: step.Stopped.AsTime().UTC(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := PrepareWebhooks(ctx, source, webhook_module.HookEventWorkflowJob, &api.WorkflowJobPayload{
|
||||
Action: status,
|
||||
WorkflowJob: &api.ActionWorkflowJob{
|
||||
ID: job.ID,
|
||||
// missing api endpoint for this location
|
||||
URL: fmt.Sprintf("%s/actions/runs/%d/jobs/%d", repo.APIURL(), job.RunID, job.ID),
|
||||
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), jobIndex),
|
||||
RunID: job.RunID,
|
||||
// Missing api endpoint for this location, artifacts are available under a nested url
|
||||
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID),
|
||||
Name: job.Name,
|
||||
Labels: job.RunsOn,
|
||||
RunAttempt: job.Attempt,
|
||||
HeadSha: job.Run.CommitSHA,
|
||||
HeadBranch: git.RefName(job.Run.Ref).BranchName(),
|
||||
Status: status,
|
||||
Conclusion: conclusion,
|
||||
RunnerID: runnerID,
|
||||
RunnerName: runnerName,
|
||||
Steps: steps,
|
||||
CreatedAt: job.Created.AsTime().UTC(),
|
||||
StartedAt: job.Started.AsTime().UTC(),
|
||||
CompletedAt: job.Stopped.AsTime().UTC(),
|
||||
},
|
||||
Action: status,
|
||||
WorkflowJob: convertedJob,
|
||||
Organization: org,
|
||||
Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
|
||||
Sender: convert.ToUser(ctx, sender, nil),
|
||||
@@ -1030,28 +974,46 @@ func (*webhookNotifier) WorkflowJobStatusUpdate(ctx context.Context, repo *repo_
|
||||
}
|
||||
}
|
||||
|
||||
func toActionStatus(status actions_model.Status) (string, string) {
|
||||
var action string
|
||||
var conclusion string
|
||||
switch status {
|
||||
// This is a naming conflict of the webhook between Gitea and GitHub Actions
|
||||
case actions_model.StatusWaiting:
|
||||
action = "queued"
|
||||
case actions_model.StatusBlocked:
|
||||
action = "waiting"
|
||||
case actions_model.StatusRunning:
|
||||
action = "in_progress"
|
||||
func (*webhookNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
|
||||
source := EventSource{
|
||||
Repository: repo,
|
||||
Owner: repo.Owner,
|
||||
}
|
||||
if status.IsDone() {
|
||||
action = "completed"
|
||||
switch status {
|
||||
case actions_model.StatusSuccess:
|
||||
conclusion = "success"
|
||||
case actions_model.StatusCancelled:
|
||||
conclusion = "cancelled"
|
||||
case actions_model.StatusFailure:
|
||||
conclusion = "failure"
|
||||
}
|
||||
|
||||
var org *api.Organization
|
||||
if repo.Owner.IsOrganization() {
|
||||
org = convert.ToOrganization(ctx, organization.OrgFromUser(repo.Owner))
|
||||
}
|
||||
|
||||
status := convert.ToWorkflowRunAction(run.Status)
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository: %v", err)
|
||||
return
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
convertedWorkflow, err := convert.GetActionWorkflow(ctx, gitRepo, repo, run.WorkflowID)
|
||||
if err != nil {
|
||||
log.Error("GetActionWorkflow: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run)
|
||||
if err != nil {
|
||||
log.Error("ToActionWorkflowRun: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := PrepareWebhooks(ctx, source, webhook_module.HookEventWorkflowRun, &api.WorkflowRunPayload{
|
||||
Action: status,
|
||||
Workflow: convertedWorkflow,
|
||||
WorkflowRun: convertedRun,
|
||||
Organization: org,
|
||||
Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
|
||||
Sender: convert.ToUser(ctx, sender, nil),
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
return action, conclusion
|
||||
}
|
||||
|
@@ -114,6 +114,10 @@ func (pc packagistConvertor) Status(_ *api.CommitStatusPayload) (PackagistPayloa
|
||||
return PackagistPayload{}, nil
|
||||
}
|
||||
|
||||
func (pc packagistConvertor) WorkflowRun(_ *api.WorkflowRunPayload) (PackagistPayload, error) {
|
||||
return PackagistPayload{}, nil
|
||||
}
|
||||
|
||||
func (pc packagistConvertor) WorkflowJob(_ *api.WorkflowJobPayload) (PackagistPayload, error) {
|
||||
return PackagistPayload{}, nil
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ type payloadConvertor[T any] interface {
|
||||
Wiki(*api.WikiPayload) (T, error)
|
||||
Package(*api.PackagePayload) (T, error)
|
||||
Status(*api.CommitStatusPayload) (T, error)
|
||||
WorkflowRun(*api.WorkflowRunPayload) (T, error)
|
||||
WorkflowJob(*api.WorkflowJobPayload) (T, error)
|
||||
}
|
||||
|
||||
@@ -81,6 +82,8 @@ func newPayload[T any](rc payloadConvertor[T], data []byte, event webhook_module
|
||||
return convertUnmarshalledJSON(rc.Package, data)
|
||||
case webhook_module.HookEventStatus:
|
||||
return convertUnmarshalledJSON(rc.Status, data)
|
||||
case webhook_module.HookEventWorkflowRun:
|
||||
return convertUnmarshalledJSON(rc.WorkflowRun, data)
|
||||
case webhook_module.HookEventWorkflowJob:
|
||||
return convertUnmarshalledJSON(rc.WorkflowJob, data)
|
||||
}
|
||||
|
@@ -173,6 +173,12 @@ func (s slackConvertor) Status(p *api.CommitStatusPayload) (SlackPayload, error)
|
||||
return s.createPayload(text, nil), nil
|
||||
}
|
||||
|
||||
func (s slackConvertor) WorkflowRun(p *api.WorkflowRunPayload) (SlackPayload, error) {
|
||||
text, _ := getWorkflowRunPayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
return s.createPayload(text, nil), nil
|
||||
}
|
||||
|
||||
func (s slackConvertor) WorkflowJob(p *api.WorkflowJobPayload) (SlackPayload, error) {
|
||||
text, _ := getWorkflowJobPayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
|
@@ -180,6 +180,12 @@ func (t telegramConvertor) Status(p *api.CommitStatusPayload) (TelegramPayload,
|
||||
return createTelegramPayloadHTML(text), nil
|
||||
}
|
||||
|
||||
func (telegramConvertor) WorkflowRun(p *api.WorkflowRunPayload) (TelegramPayload, error) {
|
||||
text, _ := getWorkflowRunPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return createTelegramPayloadHTML(text), nil
|
||||
}
|
||||
|
||||
func (telegramConvertor) WorkflowJob(p *api.WorkflowJobPayload) (TelegramPayload, error) {
|
||||
text, _ := getWorkflowJobPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
|
@@ -181,6 +181,12 @@ func (wc wechatworkConvertor) Status(p *api.CommitStatusPayload) (WechatworkPayl
|
||||
return newWechatworkMarkdownPayload(text), nil
|
||||
}
|
||||
|
||||
func (wc wechatworkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (WechatworkPayload, error) {
|
||||
text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return newWechatworkMarkdownPayload(text), nil
|
||||
}
|
||||
|
||||
func (wc wechatworkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (WechatworkPayload, error) {
|
||||
text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
|
Reference in New Issue
Block a user