Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
53 changes: 53 additions & 0 deletions cmd/hub/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package hub

import (
"fmt"
"strings"

"github.com/runpod/runpodctl/internal/api"
"github.com/runpod/runpodctl/internal/output"

"github.com/spf13/cobra"
)

var getCmd = &cobra.Command{
Use: "get <id-or-owner/name>",
Short: "get hub repo details",
Long: `get details for a hub repo by id or owner/name.

examples:
runpodctl hub get clma1kziv00064iog9u6acj6z # by listing id
runpodctl hub get runpod/worker-vllm # by owner/name`,
Args: cobra.ExactArgs(1),
RunE: runGet,
}

func runGet(cmd *cobra.Command, args []string) error {
arg := args[0]

client, err := api.NewClient()
if err != nil {
output.Error(err)
return err
}

var listing *api.Listing

if strings.Contains(arg, "/") {
parts := strings.SplitN(arg, "/", 2)
if parts[0] == "" || parts[1] == "" {
return fmt.Errorf("invalid format %q; use owner/name or a listing id", arg)
}
listing, err = client.GetListingFromRepo(parts[0], parts[1])
} else {
listing, err = client.GetListing(arg)
}

if err != nil {
output.Error(err)
return fmt.Errorf("failed to get hub repo: %w", err)
}

format := output.ParseFormat(cmd.Flag("output").Value.String())
return output.Print(listing, &output.Config{Format: format})
}
18 changes: 18 additions & 0 deletions cmd/hub/hub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package hub

import (
"github.com/spf13/cobra"
)

// Cmd is the hub command group
var Cmd = &cobra.Command{
Use: "hub",
Short: "browse the runpod hub",
Long: "browse and search the runpod hub for deployable repos",
}

func init() {
Cmd.AddCommand(listCmd)
Cmd.AddCommand(searchCmd)
Cmd.AddCommand(getCmd)
}
25 changes: 25 additions & 0 deletions cmd/hub/hub_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package hub

import (
"testing"
)

func TestHubCmd_Structure(t *testing.T) {
if Cmd.Use != "hub" {
t.Errorf("expected use 'hub', got %s", Cmd.Use)
}

expectedSubcommands := []string{"list", "search <term>", "get <id-or-owner/name>"}
for _, expected := range expectedSubcommands {
found := false
for _, cmd := range Cmd.Commands() {
if cmd.Use == expected {
found = true
break
}
}
if !found {
t.Errorf("expected subcommand %q not found", expected)
}
}
}
73 changes: 73 additions & 0 deletions cmd/hub/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package hub

import (
"github.com/runpod/runpodctl/internal/api"
"github.com/runpod/runpodctl/internal/output"

"github.com/spf13/cobra"
)

var listCmd = &cobra.Command{
Use: "list",
Short: "list hub repos",
Long: `list repos from the runpod hub.

by default shows the top 10 repos ordered by stars.

examples:
runpodctl hub list # top 10 by stars
runpodctl hub list --type SERVERLESS # only serverless repos
runpodctl hub list --type POD # only pod repos
runpodctl hub list --category ai --limit 20 # filter by category
runpodctl hub list --order-by deploys # order by deploys
runpodctl hub list --owner runpod # filter by repo owner`,
Args: cobra.NoArgs,
RunE: runList,
}

var (
listCategory string
listOrderBy string
listOrderDir string
listLimit int
listOffset int
listOwner string
listType string
)

func init() {
listCmd.Flags().StringVar(&listCategory, "category", "", "filter by category")
listCmd.Flags().StringVar(&listOrderBy, "order-by", "stars", "order by: createdAt, deploys, releasedAt, stars, updatedAt, views")
listCmd.Flags().StringVar(&listOrderDir, "order-dir", "desc", "order direction: asc or desc")
listCmd.Flags().IntVar(&listLimit, "limit", 10, "max number of results to return")
listCmd.Flags().IntVar(&listOffset, "offset", 0, "offset for pagination")
listCmd.Flags().StringVar(&listOwner, "owner", "", "filter by repo owner")
listCmd.Flags().StringVar(&listType, "type", "", "filter by type: POD or SERVERLESS")
}

func runList(cmd *cobra.Command, args []string) error {
client, err := api.NewClient()
if err != nil {
output.Error(err)
return err
}

opts := &api.ListingsOptions{
Category: listCategory,
OrderBy: listOrderBy,
OrderDirection: listOrderDir,
Limit: listLimit,
Offset: listOffset,
Owner: listOwner,
Type: listType,
}

listings, err := client.ListListings(opts)
if err != nil {
output.Error(err)
return err
}

format := output.ParseFormat(cmd.Flag("output").Value.String())
return output.Print(listings, &output.Config{Format: format})
}
78 changes: 78 additions & 0 deletions cmd/hub/search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package hub

import (
"fmt"

"github.com/runpod/runpodctl/internal/api"
"github.com/runpod/runpodctl/internal/output"

"github.com/spf13/cobra"
)

var searchCmd = &cobra.Command{
Use: "search <term>",
Short: "search hub repos",
Long: `search for repos in the runpod hub.

examples:
runpodctl hub search vllm # search for "vllm"
runpodctl hub search whisper --type SERVERLESS # search serverless repos
runpodctl hub search stable-diffusion --limit 5 # limit results`,
Args: cobra.ExactArgs(1),
RunE: runSearch,
}

var (
searchCategory string
searchOrderBy string
searchOrderDir string
searchLimit int
searchOffset int
searchOwner string
searchType string
)

func init() {
searchCmd.Flags().StringVar(&searchCategory, "category", "", "filter by category")
searchCmd.Flags().StringVar(&searchOrderBy, "order-by", "stars", "order by: createdAt, deploys, releasedAt, stars, updatedAt, views")
searchCmd.Flags().StringVar(&searchOrderDir, "order-dir", "desc", "order direction: asc or desc")
searchCmd.Flags().IntVar(&searchLimit, "limit", 10, "max number of results to return")
searchCmd.Flags().IntVar(&searchOffset, "offset", 0, "offset for pagination")
searchCmd.Flags().StringVar(&searchOwner, "owner", "", "filter by repo owner")
searchCmd.Flags().StringVar(&searchType, "type", "", "filter by type: POD or SERVERLESS")
}

func runSearch(cmd *cobra.Command, args []string) error {
searchTerm := args[0]

client, err := api.NewClient()
if err != nil {
output.Error(err)
return err
}

opts := &api.ListingsOptions{
SearchQuery: searchTerm,
Category: searchCategory,
OrderBy: searchOrderBy,
OrderDirection: searchOrderDir,
Limit: searchLimit,
Offset: searchOffset,
Owner: searchOwner,
Type: searchType,
}

listings, err := client.ListListings(opts)
if err != nil {
output.Error(err)
return err
}

if len(listings) == 0 {
fmt.Printf("no hub repos found matching %q\n", searchTerm)
return nil
}

format := output.ParseFormat(cmd.Flag("output").Value.String())
return output.Print(listings, &output.Config{Format: format})
}
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/runpod/runpodctl/cmd/datacenter"
"github.com/runpod/runpodctl/cmd/doctor"
"github.com/runpod/runpodctl/cmd/gpu"
"github.com/runpod/runpodctl/cmd/hub"
"github.com/runpod/runpodctl/cmd/legacy"
"github.com/runpod/runpodctl/cmd/model"
"github.com/runpod/runpodctl/cmd/pod"
Expand Down Expand Up @@ -43,6 +44,7 @@ resources:
pod manage gpu pods
serverless manage serverless endpoints (alias: sls)
template manage templates (alias: tpl)
hub browse the runpod hub
model manage model repository
network-volume manage network volumes (alias: nv)
registry manage container registry auth (alias: reg)
Expand Down Expand Up @@ -87,6 +89,7 @@ func registerCommands() {
rootCmd.AddCommand(model.Cmd)
rootCmd.AddCommand(volume.Cmd)
rootCmd.AddCommand(registry.Cmd)
rootCmd.AddCommand(hub.Cmd)

// Info commands
rootCmd.AddCommand(user.Cmd)
Expand Down
Loading
Loading