Skip to content

Commit 4c56071

Browse files
authored
Merge branch 'main' into bugfix/variable_router
2 parents 7470044 + 30993e9 commit 4c56071

File tree

13 files changed

+1748
-148
lines changed

13 files changed

+1748
-148
lines changed

modules/structs/repo_actions.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,36 @@ type ActionTaskResponse struct {
3232
Entries []*ActionTask `json:"workflow_runs"`
3333
TotalCount int64 `json:"total_count"`
3434
}
35+
36+
// CreateActionWorkflowDispatch represents the payload for triggering a workflow dispatch event
37+
// swagger:model
38+
type CreateActionWorkflowDispatch struct {
39+
// required: true
40+
// example: refs/heads/main
41+
Ref string `json:"ref" binding:"Required"`
42+
// required: false
43+
Inputs map[string]string `json:"inputs,omitempty"`
44+
}
45+
46+
// ActionWorkflow represents a ActionWorkflow
47+
type ActionWorkflow struct {
48+
ID string `json:"id"`
49+
Name string `json:"name"`
50+
Path string `json:"path"`
51+
State string `json:"state"`
52+
// swagger:strfmt date-time
53+
CreatedAt time.Time `json:"created_at"`
54+
// swagger:strfmt date-time
55+
UpdatedAt time.Time `json:"updated_at"`
56+
URL string `json:"url"`
57+
HTMLURL string `json:"html_url"`
58+
BadgeURL string `json:"badge_url"`
59+
// swagger:strfmt date-time
60+
DeletedAt time.Time `json:"deleted_at,omitempty"`
61+
}
62+
63+
// ActionWorkflowResponse returns a ActionWorkflow
64+
type ActionWorkflowResponse struct {
65+
Workflows []*ActionWorkflow `json:"workflows"`
66+
TotalCount int64 `json:"total_count"`
67+
}

modules/util/error.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ func (w SilentWrap) Unwrap() error {
3636
return w.Err
3737
}
3838

39+
type LocaleWrap struct {
40+
err error
41+
TrKey string
42+
TrArgs []any
43+
}
44+
45+
// Error returns the message
46+
func (w LocaleWrap) Error() string {
47+
return w.err.Error()
48+
}
49+
50+
// Unwrap returns the underlying error
51+
func (w LocaleWrap) Unwrap() error {
52+
return w.err
53+
}
54+
3955
// NewSilentWrapErrorf returns an error that formats as the given text but unwraps as the provided error
4056
func NewSilentWrapErrorf(unwrap error, message string, args ...any) error {
4157
if len(args) == 0 {
@@ -63,3 +79,16 @@ func NewAlreadyExistErrorf(message string, args ...any) error {
6379
func NewNotExistErrorf(message string, args ...any) error {
6480
return NewSilentWrapErrorf(ErrNotExist, message, args...)
6581
}
82+
83+
// ErrWrapLocale wraps an err with a translation key and arguments
84+
func ErrWrapLocale(err error, trKey string, trArgs ...any) error {
85+
return LocaleWrap{err: err, TrKey: trKey, TrArgs: trArgs}
86+
}
87+
88+
func ErrAsLocale(err error) *LocaleWrap {
89+
var e LocaleWrap
90+
if errors.As(err, &e) {
91+
return &e
92+
}
93+
return nil
94+
}

routers/api/v1/api.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,11 +1155,17 @@ func Routes() *web.Router {
11551155
m.Post("/accept", repo.AcceptTransfer)
11561156
m.Post("/reject", repo.RejectTransfer)
11571157
}, reqToken())
1158-
addActionsRoutes(
1159-
m,
1160-
reqOwner(),
1161-
repo.NewAction(),
1162-
)
1158+
1159+
addActionsRoutes(m, reqOwner(), repo.NewAction()) // it adds the routes for secrets/variables and runner management
1160+
1161+
m.Group("/actions/workflows", func() {
1162+
m.Get("", repo.ActionsListRepositoryWorkflows)
1163+
m.Get("/{workflow_id}", repo.ActionsGetWorkflow)
1164+
m.Put("/{workflow_id}/disable", reqRepoWriter(unit.TypeActions), repo.ActionsDisableWorkflow)
1165+
m.Put("/{workflow_id}/enable", reqRepoWriter(unit.TypeActions), repo.ActionsEnableWorkflow)
1166+
m.Post("/{workflow_id}/dispatches", reqRepoWriter(unit.TypeActions), bind(api.CreateActionWorkflowDispatch{}), repo.ActionsDispatchWorkflow)
1167+
}, context.ReferencesGitRepo(), reqToken(), reqRepoReader(unit.TypeActions))
1168+
11631169
m.Group("/hooks/git", func() {
11641170
m.Combo("").Get(repo.ListGitHooks)
11651171
m.Group("/{id}", func() {

routers/api/v1/repo/action.go

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package repo
66
import (
77
"errors"
88
"net/http"
9+
"strings"
910

1011
actions_model "code.gitea.io/gitea/models/actions"
1112
"code.gitea.io/gitea/models/db"
@@ -19,6 +20,8 @@ import (
1920
"code.gitea.io/gitea/services/context"
2021
"code.gitea.io/gitea/services/convert"
2122
secret_service "code.gitea.io/gitea/services/secrets"
23+
24+
"github.com/nektos/act/pkg/model"
2225
)
2326

2427
// ListActionsSecrets list an repo's actions secrets
@@ -585,3 +588,270 @@ func ListActionTasks(ctx *context.APIContext) {
585588

586589
ctx.JSON(http.StatusOK, &res)
587590
}
591+
592+
func ActionsListRepositoryWorkflows(ctx *context.APIContext) {
593+
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows repository ActionsListRepositoryWorkflows
594+
// ---
595+
// summary: List repository workflows
596+
// produces:
597+
// - application/json
598+
// parameters:
599+
// - name: owner
600+
// in: path
601+
// description: owner of the repo
602+
// type: string
603+
// required: true
604+
// - name: repo
605+
// in: path
606+
// description: name of the repo
607+
// type: string
608+
// required: true
609+
// responses:
610+
// "200":
611+
// "$ref": "#/responses/ActionWorkflowList"
612+
// "400":
613+
// "$ref": "#/responses/error"
614+
// "403":
615+
// "$ref": "#/responses/forbidden"
616+
// "404":
617+
// "$ref": "#/responses/notFound"
618+
// "422":
619+
// "$ref": "#/responses/validationError"
620+
// "500":
621+
// "$ref": "#/responses/error"
622+
623+
workflows, err := actions_service.ListActionWorkflows(ctx)
624+
if err != nil {
625+
ctx.Error(http.StatusInternalServerError, "ListActionWorkflows", err)
626+
return
627+
}
628+
629+
ctx.JSON(http.StatusOK, &api.ActionWorkflowResponse{Workflows: workflows, TotalCount: int64(len(workflows))})
630+
}
631+
632+
func ActionsGetWorkflow(ctx *context.APIContext) {
633+
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} repository ActionsGetWorkflow
634+
// ---
635+
// summary: Get a workflow
636+
// produces:
637+
// - application/json
638+
// parameters:
639+
// - name: owner
640+
// in: path
641+
// description: owner of the repo
642+
// type: string
643+
// required: true
644+
// - name: repo
645+
// in: path
646+
// description: name of the repo
647+
// type: string
648+
// required: true
649+
// - name: workflow_id
650+
// in: path
651+
// description: id of the workflow
652+
// type: string
653+
// required: true
654+
// responses:
655+
// "200":
656+
// "$ref": "#/responses/ActionWorkflow"
657+
// "400":
658+
// "$ref": "#/responses/error"
659+
// "403":
660+
// "$ref": "#/responses/forbidden"
661+
// "404":
662+
// "$ref": "#/responses/notFound"
663+
// "422":
664+
// "$ref": "#/responses/validationError"
665+
// "500":
666+
// "$ref": "#/responses/error"
667+
668+
workflowID := ctx.PathParam("workflow_id")
669+
workflow, err := actions_service.GetActionWorkflow(ctx, workflowID)
670+
if err != nil {
671+
if errors.Is(err, util.ErrNotExist) {
672+
ctx.Error(http.StatusNotFound, "GetActionWorkflow", err)
673+
} else {
674+
ctx.Error(http.StatusInternalServerError, "GetActionWorkflow", err)
675+
}
676+
return
677+
}
678+
679+
ctx.JSON(http.StatusOK, workflow)
680+
}
681+
682+
func ActionsDisableWorkflow(ctx *context.APIContext) {
683+
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable repository ActionsDisableWorkflow
684+
// ---
685+
// summary: Disable a workflow
686+
// produces:
687+
// - application/json
688+
// parameters:
689+
// - name: owner
690+
// in: path
691+
// description: owner of the repo
692+
// type: string
693+
// required: true
694+
// - name: repo
695+
// in: path
696+
// description: name of the repo
697+
// type: string
698+
// required: true
699+
// - name: workflow_id
700+
// in: path
701+
// description: id of the workflow
702+
// type: string
703+
// required: true
704+
// responses:
705+
// "204":
706+
// description: No Content
707+
// "400":
708+
// "$ref": "#/responses/error"
709+
// "403":
710+
// "$ref": "#/responses/forbidden"
711+
// "404":
712+
// "$ref": "#/responses/notFound"
713+
// "422":
714+
// "$ref": "#/responses/validationError"
715+
716+
workflowID := ctx.PathParam("workflow_id")
717+
err := actions_service.EnableOrDisableWorkflow(ctx, workflowID, false)
718+
if err != nil {
719+
if errors.Is(err, util.ErrNotExist) {
720+
ctx.Error(http.StatusNotFound, "DisableActionWorkflow", err)
721+
} else {
722+
ctx.Error(http.StatusInternalServerError, "DisableActionWorkflow", err)
723+
}
724+
return
725+
}
726+
727+
ctx.Status(http.StatusNoContent)
728+
}
729+
730+
func ActionsDispatchWorkflow(ctx *context.APIContext) {
731+
// swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches repository ActionsDispatchWorkflow
732+
// ---
733+
// summary: Create a workflow dispatch event
734+
// produces:
735+
// - application/json
736+
// parameters:
737+
// - name: owner
738+
// in: path
739+
// description: owner of the repo
740+
// type: string
741+
// required: true
742+
// - name: repo
743+
// in: path
744+
// description: name of the repo
745+
// type: string
746+
// required: true
747+
// - name: workflow_id
748+
// in: path
749+
// description: id of the workflow
750+
// type: string
751+
// required: true
752+
// - name: body
753+
// in: body
754+
// schema:
755+
// "$ref": "#/definitions/CreateActionWorkflowDispatch"
756+
// responses:
757+
// "204":
758+
// description: No Content
759+
// "400":
760+
// "$ref": "#/responses/error"
761+
// "403":
762+
// "$ref": "#/responses/forbidden"
763+
// "404":
764+
// "$ref": "#/responses/notFound"
765+
// "422":
766+
// "$ref": "#/responses/validationError"
767+
768+
workflowID := ctx.PathParam("workflow_id")
769+
opt := web.GetForm(ctx).(*api.CreateActionWorkflowDispatch)
770+
if opt.Ref == "" {
771+
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("ref is required parameter"))
772+
return
773+
}
774+
775+
err := actions_service.DispatchActionWorkflow(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, workflowID, opt.Ref, func(workflowDispatch *model.WorkflowDispatch, inputs map[string]any) error {
776+
if strings.Contains(ctx.Req.Header.Get("Content-Type"), "form-urlencoded") {
777+
// The chi framework's "Binding" doesn't support to bind the form map values into a map[string]string
778+
// So we have to manually read the `inputs[key]` from the form
779+
for name, config := range workflowDispatch.Inputs {
780+
value := ctx.FormString("inputs["+name+"]", config.Default)
781+
inputs[name] = value
782+
}
783+
} else {
784+
for name, config := range workflowDispatch.Inputs {
785+
value, ok := opt.Inputs[name]
786+
if ok {
787+
inputs[name] = value
788+
} else {
789+
inputs[name] = config.Default
790+
}
791+
}
792+
}
793+
return nil
794+
})
795+
if err != nil {
796+
if errors.Is(err, util.ErrNotExist) {
797+
ctx.Error(http.StatusNotFound, "DispatchActionWorkflow", err)
798+
} else if errors.Is(err, util.ErrPermissionDenied) {
799+
ctx.Error(http.StatusForbidden, "DispatchActionWorkflow", err)
800+
} else {
801+
ctx.Error(http.StatusInternalServerError, "DispatchActionWorkflow", err)
802+
}
803+
return
804+
}
805+
806+
ctx.Status(http.StatusNoContent)
807+
}
808+
809+
func ActionsEnableWorkflow(ctx *context.APIContext) {
810+
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable repository ActionsEnableWorkflow
811+
// ---
812+
// summary: Enable a workflow
813+
// produces:
814+
// - application/json
815+
// parameters:
816+
// - name: owner
817+
// in: path
818+
// description: owner of the repo
819+
// type: string
820+
// required: true
821+
// - name: repo
822+
// in: path
823+
// description: name of the repo
824+
// type: string
825+
// required: true
826+
// - name: workflow_id
827+
// in: path
828+
// description: id of the workflow
829+
// type: string
830+
// required: true
831+
// responses:
832+
// "204":
833+
// description: No Content
834+
// "400":
835+
// "$ref": "#/responses/error"
836+
// "403":
837+
// "$ref": "#/responses/forbidden"
838+
// "404":
839+
// "$ref": "#/responses/notFound"
840+
// "409":
841+
// "$ref": "#/responses/conflict"
842+
// "422":
843+
// "$ref": "#/responses/validationError"
844+
845+
workflowID := ctx.PathParam("workflow_id")
846+
err := actions_service.EnableOrDisableWorkflow(ctx, workflowID, true)
847+
if err != nil {
848+
if errors.Is(err, util.ErrNotExist) {
849+
ctx.Error(http.StatusNotFound, "EnableActionWorkflow", err)
850+
} else {
851+
ctx.Error(http.StatusInternalServerError, "EnableActionWorkflow", err)
852+
}
853+
return
854+
}
855+
856+
ctx.Status(http.StatusNoContent)
857+
}

0 commit comments

Comments
 (0)