diff --git a/internal/cmd/service/list/list.go b/internal/cmd/service/list/list.go index 3eb0c6c..38f52de 100644 --- a/internal/cmd/service/list/list.go +++ b/internal/cmd/service/list/list.go @@ -27,7 +27,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { Args: cobra.NoArgs, Aliases: []string{"ls"}, PreRunE: util.RunEChain( - util.NeedProjectContextWhenNonInteractive(f), + util.NeedProjectContextWhenNonInteractive(f, &opts.projectID), util.DefaultIDByContext(ctx.GetEnvironment(), &opts.environmentID), ), RunE: func(cmd *cobra.Command, args []string) error { @@ -35,6 +35,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { }, } + cmd.Flags().StringVar(&opts.projectID, "project-id", opts.projectID, "Project ID") util.AddEnvOfServiceParam(cmd, &opts.environmentID) return cmd @@ -49,10 +50,12 @@ func runList(f *cmdutil.Factory, opts *Options) error { } func runListInteractive(f *cmdutil.Factory, opts *Options) error { - // fetch project id from context - opts.projectID = f.Config.GetContext().GetProject().GetID() + // if project id is not set by flag, fetch from context + if opts.projectID == "" { + opts.projectID = f.Config.GetContext().GetProject().GetID() + } - // if project id is not set, prompt to select one + // if project id is still not set, prompt to select one if _, err := f.ParamFiller.Project(&opts.projectID); err != nil { return err } diff --git a/internal/cmd/template/search/search.go b/internal/cmd/template/search/search.go index beab292..52cff1b 100644 --- a/internal/cmd/template/search/search.go +++ b/internal/cmd/template/search/search.go @@ -62,17 +62,41 @@ func runSearch(f *cmdutil.Factory, opts Options) error { s.Stop() keyword := strings.ToLower(opts.keyword) - var matched model.Templates + + type scoredTemplate struct { + template *model.Template + score int // higher = more relevant + } + + var matched []scoredTemplate for _, t := range allTemplates { name := strings.ToLower(t.Name) desc := strings.ToLower(t.Description) - if strings.Contains(name, keyword) || strings.Contains(desc, keyword) { - matched = append(matched, t) + + score := 0 + if strings.Contains(name, keyword) { + score = 3 + } else if strings.Contains(desc, keyword) { + score = 2 + } else { + for _, svc := range t.Services { + if strings.Contains(strings.ToLower(svc.Name), keyword) { + score = 1 + break + } + } + } + + if score > 0 { + matched = append(matched, scoredTemplate{template: t, score: score}) } } sort.Slice(matched, func(i, j int) bool { - return matched[i].DeploymentCnt > matched[j].DeploymentCnt + if matched[i].score != matched[j].score { + return matched[i].score > matched[j].score + } + return matched[i].template.DeploymentCnt > matched[j].template.DeploymentCnt }) if len(matched) == 0 { @@ -82,7 +106,8 @@ func runSearch(f *cmdutil.Factory, opts Options) error { header := []string{"Code", "Name", "Description", "Deployments"} rows := make([][]string, 0, len(matched)) - for _, t := range matched { + for _, m := range matched { + t := m.template rows = append(rows, []string{t.Code, t.Name, t.Description, strconv.Itoa(t.DeploymentCnt)}) } f.Printer.Table(header, rows) diff --git a/internal/util/runE.go b/internal/util/runE.go index 13eeaf2..80cc04b 100644 --- a/internal/util/runE.go +++ b/internal/util/runE.go @@ -8,9 +8,13 @@ import ( "github.com/zeabur/cli/pkg/zcontext" ) -// NeedProjectContextWhenNonInteractive checks if the project context is set in the non-interactive mode -func NeedProjectContextWhenNonInteractive(f *cmdutil.Factory) CobraRunE { +// NeedProjectContextWhenNonInteractive checks if the project context is set in the non-interactive mode. +// If overrideID is provided and non-empty, the check is skipped (the caller already has a project ID from a flag). +func NeedProjectContextWhenNonInteractive(f *cmdutil.Factory, overrideID ...*string) CobraRunE { return func(cmd *cobra.Command, args []string) error { + if len(overrideID) > 0 && overrideID[0] != nil && *overrideID[0] != "" { + return nil + } if !f.Interactive && f.Config.GetContext().GetProject().Empty() { return errors.New("please run first") } diff --git a/pkg/model/template.go b/pkg/model/template.go index f11c685..33b0c0e 100644 --- a/pkg/model/template.go +++ b/pkg/model/template.go @@ -6,15 +6,21 @@ import ( "github.com/zeabur/cli/pkg/util" ) +// TemplateServiceRef is a minimal reference to a service in a template, used for GraphQL queries. +type TemplateServiceRef struct { + Name string `graphql:"name"` +} + type Template struct { - CreatedAt time.Time `graphql:"createdAt"` - DeploymentCnt int `graphql:"deploymentCnt"` - Code string `graphql:"code"` - Description string `graphql:"description"` - Name string `graphql:"name"` - PreviewURL string `graphql:"previewURL"` - Readme string `graphql:"readme"` - Tags []string `graphql:"tags"` + CreatedAt time.Time `graphql:"createdAt"` + DeploymentCnt int `graphql:"deploymentCnt"` + Code string `graphql:"code"` + Description string `graphql:"description"` + Name string `graphql:"name"` + PreviewURL string `graphql:"previewURL"` + Readme string `graphql:"readme"` + Tags []string `graphql:"tags"` + Services []TemplateServiceRef `graphql:"services"` } type TemplateConnection struct {