Skip to content

Commit e137972

Browse files
Merge branch 'main' into artifacts-download-api
2 parents 7a35443 + 30993e9 commit e137972

File tree

21 files changed

+538
-314
lines changed

21 files changed

+538
-314
lines changed

models/issues/issue.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
user_model "code.gitea.io/gitea/models/user"
1818
"code.gitea.io/gitea/modules/container"
1919
"code.gitea.io/gitea/modules/log"
20+
"code.gitea.io/gitea/modules/optional"
2021
"code.gitea.io/gitea/modules/setting"
2122
api "code.gitea.io/gitea/modules/structs"
2223
"code.gitea.io/gitea/modules/timeutil"
@@ -501,6 +502,45 @@ func GetIssueByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
501502
return issue, nil
502503
}
503504

505+
func isPullToCond(isPull optional.Option[bool]) builder.Cond {
506+
if isPull.Has() {
507+
return builder.Eq{"is_pull": isPull.Value()}
508+
}
509+
return builder.NewCond()
510+
}
511+
512+
func FindLatestUpdatedIssues(ctx context.Context, repoID int64, isPull optional.Option[bool], pageSize int) (IssueList, error) {
513+
issues := make([]*Issue, 0, pageSize)
514+
err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
515+
And(isPullToCond(isPull)).
516+
OrderBy("updated_unix DESC").
517+
Limit(pageSize).
518+
Find(&issues)
519+
return issues, err
520+
}
521+
522+
func FindIssuesSuggestionByKeyword(ctx context.Context, repoID int64, keyword string, isPull optional.Option[bool], excludedID int64, pageSize int) (IssueList, error) {
523+
cond := builder.NewCond()
524+
if excludedID > 0 {
525+
cond = cond.And(builder.Neq{"`id`": excludedID})
526+
}
527+
528+
// It seems that GitHub searches both title and content (maybe sorting by the search engine's ranking system?)
529+
// The first PR (https://github.com/go-gitea/gitea/pull/32327) uses "search indexer" to search "name(title) + content"
530+
// But it seems that searching "content" (especially LIKE by DB engine) generates worse (unusable) results.
531+
// So now (https://github.com/go-gitea/gitea/pull/33538) it only searches "name(title)", leave the improvements to the future.
532+
cond = cond.And(db.BuildCaseInsensitiveLike("`name`", keyword))
533+
534+
issues := make([]*Issue, 0, pageSize)
535+
err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
536+
And(isPullToCond(isPull)).
537+
And(cond).
538+
OrderBy("updated_unix DESC, `index` DESC").
539+
Limit(pageSize).
540+
Find(&issues)
541+
return issues, err
542+
}
543+
504544
// GetIssueWithAttrsByIndex returns issue by index in a repository.
505545
func GetIssueWithAttrsByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
506546
issue, err := GetIssueByIndex(ctx, repoID, index)

modules/structs/repo_actions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type CreateActionWorkflowDispatch struct {
4040
// example: refs/heads/main
4141
Ref string `json:"ref" binding:"Required"`
4242
// required: false
43-
Inputs map[string]any `json:"inputs,omitempty"`
43+
Inputs map[string]string `json:"inputs,omitempty"`
4444
}
4545

4646
// ActionWorkflow represents a ActionWorkflow

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+
}

options/locale/locale_en-US.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,13 @@ show_only_public = Showing only public
385385

386386
issues.in_your_repos = In your repositories
387387

388+
guide_title = No Activity
389+
guide_desc = You are currently not following any repositories or users, so there is no content to display. You can explore repositories or users of interest from the links below.
390+
explore_repos = Explore repositories
391+
explore_users = Explore users
392+
empty_org = There are no organizations yet.
393+
empty_repo = There are no repositories yet.
394+
388395
[explore]
389396
repos = Repositories
390397
users = Users

routers/api/v1/api.go

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -915,21 +915,6 @@ func Routes() *web.Router {
915915
})
916916
}
917917

918-
addActionsWorkflowRoutes := func(
919-
m *web.Router,
920-
actw actions.WorkflowAPI,
921-
) {
922-
m.Group("/actions", func() {
923-
m.Group("/workflows", func() {
924-
m.Get("", reqToken(), actw.ListRepositoryWorkflows)
925-
m.Get("/{workflow_id}", reqToken(), actw.GetWorkflow)
926-
m.Put("/{workflow_id}/disable", reqToken(), reqRepoWriter(unit.TypeActions), actw.DisableWorkflow)
927-
m.Post("/{workflow_id}/dispatches", reqToken(), reqRepoWriter(unit.TypeActions), bind(api.CreateActionWorkflowDispatch{}), actw.DispatchWorkflow)
928-
m.Put("/{workflow_id}/enable", reqToken(), reqRepoWriter(unit.TypeActions), actw.EnableWorkflow)
929-
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeActions))
930-
})
931-
}
932-
933918
m.Group("", func() {
934919
// Miscellaneous (no scope required)
935920
if setting.API.EnableSwagger {
@@ -1170,15 +1155,17 @@ func Routes() *web.Router {
11701155
m.Post("/accept", repo.AcceptTransfer)
11711156
m.Post("/reject", repo.RejectTransfer)
11721157
}, reqToken())
1173-
addActionsRoutes(
1174-
m,
1175-
reqOwner(),
1176-
repo.NewAction(),
1177-
)
1178-
addActionsWorkflowRoutes(
1179-
m,
1180-
repo.NewActionWorkflow(),
1181-
)
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+
11821169
m.Group("/hooks/git", func() {
11831170
m.Combo("").Get(repo.ListGitHooks)
11841171
m.Group("/{id}", func() {

routers/api/v1/repo/action.go

Lines changed: 51 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ package repo
55

66
import (
77
"errors"
8-
"fmt"
98
"net/http"
109
"net/url"
10+
"strings"
1111

1212
actions_model "code.gitea.io/gitea/models/actions"
1313
"code.gitea.io/gitea/models/db"
@@ -589,16 +589,8 @@ func ListActionTasks(ctx *context.APIContext) {
589589
ctx.JSON(http.StatusOK, &res)
590590
}
591591

592-
// ActionWorkflow implements actions_service.WorkflowAPI
593-
type ActionWorkflow struct{}
594-
595-
// NewActionWorkflow creates a new ActionWorkflow service
596-
func NewActionWorkflow() actions_service.WorkflowAPI {
597-
return ActionWorkflow{}
598-
}
599-
600-
func (a ActionWorkflow) ListRepositoryWorkflows(ctx *context.APIContext) {
601-
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows repository ListRepositoryWorkflows
592+
func ActionsListRepositoryWorkflows(ctx *context.APIContext) {
593+
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows repository ActionsListRepositoryWorkflows
602594
// ---
603595
// summary: List repository workflows
604596
// produces:
@@ -637,8 +629,8 @@ func (a ActionWorkflow) ListRepositoryWorkflows(ctx *context.APIContext) {
637629
ctx.JSON(http.StatusOK, &api.ActionWorkflowResponse{Workflows: workflows, TotalCount: int64(len(workflows))})
638630
}
639631

640-
func (a ActionWorkflow) GetWorkflow(ctx *context.APIContext) {
641-
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} repository GetWorkflow
632+
func ActionsGetWorkflow(ctx *context.APIContext) {
633+
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} repository ActionsGetWorkflow
642634
// ---
643635
// summary: Get a workflow
644636
// produces:
@@ -674,27 +666,21 @@ func (a ActionWorkflow) GetWorkflow(ctx *context.APIContext) {
674666
// "$ref": "#/responses/error"
675667

676668
workflowID := ctx.PathParam("workflow_id")
677-
if len(workflowID) == 0 {
678-
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
679-
return
680-
}
681-
682669
workflow, err := actions_service.GetActionWorkflow(ctx, workflowID)
683670
if err != nil {
684-
ctx.Error(http.StatusInternalServerError, "GetActionWorkflow", err)
685-
return
686-
}
687-
688-
if workflow == nil {
689-
ctx.Error(http.StatusNotFound, "GetActionWorkflow", err)
671+
if errors.Is(err, util.ErrNotExist) {
672+
ctx.Error(http.StatusNotFound, "GetActionWorkflow", err)
673+
} else {
674+
ctx.Error(http.StatusInternalServerError, "GetActionWorkflow", err)
675+
}
690676
return
691677
}
692678

693679
ctx.JSON(http.StatusOK, workflow)
694680
}
695681

696-
func (a ActionWorkflow) DisableWorkflow(ctx *context.APIContext) {
697-
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable repository DisableWorkflow
682+
func ActionsDisableWorkflow(ctx *context.APIContext) {
683+
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable repository ActionsDisableWorkflow
698684
// ---
699685
// summary: Disable a workflow
700686
// produces:
@@ -728,22 +714,21 @@ func (a ActionWorkflow) DisableWorkflow(ctx *context.APIContext) {
728714
// "$ref": "#/responses/validationError"
729715

730716
workflowID := ctx.PathParam("workflow_id")
731-
if len(workflowID) == 0 {
732-
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
733-
return
734-
}
735-
736-
err := actions_service.DisableActionWorkflow(ctx, workflowID)
717+
err := actions_service.EnableOrDisableWorkflow(ctx, workflowID, false)
737718
if err != nil {
738-
ctx.Error(http.StatusInternalServerError, "DisableActionWorkflow", err)
719+
if errors.Is(err, util.ErrNotExist) {
720+
ctx.Error(http.StatusNotFound, "DisableActionWorkflow", err)
721+
} else {
722+
ctx.Error(http.StatusInternalServerError, "DisableActionWorkflow", err)
723+
}
739724
return
740725
}
741726

742727
ctx.Status(http.StatusNoContent)
743728
}
744729

745-
func (a ActionWorkflow) DispatchWorkflow(ctx *context.APIContext) {
746-
// swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches repository DispatchWorkflow
730+
func ActionsDispatchWorkflow(ctx *context.APIContext) {
731+
// swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches repository ActionsDispatchWorkflow
747732
// ---
748733
// summary: Create a workflow dispatch event
749734
// produces:
@@ -780,60 +765,49 @@ func (a ActionWorkflow) DispatchWorkflow(ctx *context.APIContext) {
780765
// "422":
781766
// "$ref": "#/responses/validationError"
782767

783-
opt := web.GetForm(ctx).(*api.CreateActionWorkflowDispatch)
784-
785768
workflowID := ctx.PathParam("workflow_id")
786-
if len(workflowID) == 0 {
787-
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
788-
return
789-
}
790-
791-
ref := opt.Ref
792-
if len(ref) == 0 {
769+
opt := web.GetForm(ctx).(*api.CreateActionWorkflowDispatch)
770+
if opt.Ref == "" {
793771
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("ref is required parameter"))
794772
return
795773
}
796774

797-
err := actions_service.DispatchActionWorkflow(&context.Context{
798-
Base: ctx.Base,
799-
Doer: ctx.Doer,
800-
Repo: ctx.Repo,
801-
}, workflowID, ref, func(workflowDispatch *model.WorkflowDispatch, inputs *map[string]any) error {
802-
if workflowDispatch != nil {
803-
// TODO figure out why the inputs map is empty for url form encoding workaround
804-
if opt.Inputs == nil {
805-
for name, config := range workflowDispatch.Inputs {
806-
value := ctx.FormString("inputs["+name+"]", config.Default)
807-
(*inputs)[name] = value
808-
}
809-
} else {
810-
for name, config := range workflowDispatch.Inputs {
811-
value, ok := opt.Inputs[name]
812-
if ok {
813-
(*inputs)[name] = value
814-
} else {
815-
(*inputs)[name] = config.Default
816-
}
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
817790
}
818791
}
819792
}
820793
return nil
821794
})
822795
if err != nil {
823-
if terr, ok := err.(*actions_service.TranslateableError); ok {
824-
msg := ctx.Locale.TrString(terr.Translation, terr.Args...)
825-
ctx.Error(terr.GetCode(), msg, fmt.Errorf("%s", msg))
826-
return
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)
827802
}
828-
ctx.Error(http.StatusInternalServerError, err.Error(), err)
829803
return
830804
}
831805

832806
ctx.Status(http.StatusNoContent)
833807
}
834808

835-
func (a ActionWorkflow) EnableWorkflow(ctx *context.APIContext) {
836-
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable repository EnableWorkflow
809+
func ActionsEnableWorkflow(ctx *context.APIContext) {
810+
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable repository ActionsEnableWorkflow
837811
// ---
838812
// summary: Enable a workflow
839813
// produces:
@@ -869,14 +843,13 @@ func (a ActionWorkflow) EnableWorkflow(ctx *context.APIContext) {
869843
// "$ref": "#/responses/validationError"
870844

871845
workflowID := ctx.PathParam("workflow_id")
872-
if len(workflowID) == 0 {
873-
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
874-
return
875-
}
876-
877-
err := actions_service.EnableActionWorkflow(ctx, workflowID)
846+
err := actions_service.EnableOrDisableWorkflow(ctx, workflowID, true)
878847
if err != nil {
879-
ctx.Error(http.StatusInternalServerError, "EnableActionWorkflow", err)
848+
if errors.Is(err, util.ErrNotExist) {
849+
ctx.Error(http.StatusNotFound, "EnableActionWorkflow", err)
850+
} else {
851+
ctx.Error(http.StatusInternalServerError, "EnableActionWorkflow", err)
852+
}
880853
return
881854
}
882855

0 commit comments

Comments
 (0)