Skip to content

Commit 98ab11f

Browse files
feat: add registriy list cmd (#19)
* registry list cmd * added verbose for detailed view * updated the registry api
1 parent f859554 commit 98ab11f

File tree

4 files changed

+180
-4
lines changed

4 files changed

+180
-4
lines changed

CLAUDE.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
This is a Go-based CLI tool for interacting with JuliaHub, a platform for Julia computing. The CLI provides commands for authentication, dataset management, project management, user information, Git integration, and Julia integration.
7+
This is a Go-based CLI tool for interacting with JuliaHub, a platform for Julia computing. The CLI provides commands for authentication, dataset management, registry management, project management, user information, Git integration, and Julia integration.
88

99
## Architecture
1010

@@ -13,6 +13,7 @@ The application follows a command-line interface pattern using the Cobra library
1313
- **main.go**: Core CLI structure with command definitions and configuration management
1414
- **auth.go**: OAuth2 device flow authentication with JWT token handling
1515
- **datasets.go**: Dataset operations (list, download, upload, status) with REST API integration
16+
- **registries.go**: Registry operations (list) with REST API integration
1617
- **projects.go**: Project management using GraphQL API with user filtering
1718
- **user.go**: User information retrieval using GraphQL API
1819
- **git.go**: Git integration (clone, push, fetch, pull) with JuliaHub authentication
@@ -29,14 +30,15 @@ The application follows a command-line interface pattern using the Cobra library
2930
- Stores tokens securely in `~/.juliahub` with 0600 permissions
3031

3132
2. **API Integration**:
32-
- **REST API**: Used for dataset operations (`/api/v1/datasets`, `/datasets/{uuid}/url/{version}`)
33+
- **REST API**: Used for dataset operations (`/api/v1/datasets`, `/datasets/{uuid}/url/{version}`) and registry operations (`/api/v1/ui/registries/descriptions`)
3334
- **GraphQL API**: Used for projects and user info (`/v1/graphql`)
3435
- **Headers**: All GraphQL requests require `X-Hasura-Role: jhuser` header
3536
- **Authentication**: Uses ID tokens (`token.IDToken`) for API calls
3637

3738
3. **Command Structure**:
3839
- `jh auth`: Authentication commands (login, refresh, status, env)
3940
- `jh dataset`: Dataset operations (list, download, upload, status)
41+
- `jh registry`: Registry operations (list with REST API, supports verbose mode)
4042
- `jh project`: Project management (list with GraphQL, supports user filtering)
4143
- `jh user`: User information (info with GraphQL)
4244
- `jh clone`: Git clone with JuliaHub authentication and project name resolution
@@ -84,6 +86,12 @@ go run . dataset download <dataset-name>
8486
go run . dataset upload --new ./file.tar.gz
8587
```
8688

89+
### Test registry operations
90+
```bash
91+
go run . registry list
92+
go run . registry list --verbose
93+
```
94+
8795
### Test project and user operations
8896
```bash
8997
go run . project list
@@ -278,6 +286,7 @@ jh run setup
278286
- Clone command supports `project` (without username) and defaults to the logged-in user's username
279287
- Folder naming conflicts are resolved with automatic numbering (project-1, project-2, etc.)
280288
- Credential helper follows Git protocol: responds only to JuliaHub URLs, ignores others
289+
- Registry list output is concise by default (UUID and Name only); use `--verbose` flag for detailed information (owner, creation date, package count, description)
281290

282291
## Implementation Details
283292

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ A command-line interface for interacting with JuliaHub, a platform for Julia com
66

77
- **Authentication**: OAuth2 device flow authentication with JWT token handling
88
- **Dataset Management**: List, download, upload, and check status of datasets
9+
- **Registry Management**: List and manage Julia package registries
910
- **Project Management**: List and filter projects using GraphQL API
1011
- **Git Integration**: Clone, push, fetch, and pull with automatic JuliaHub authentication
1112
- **Julia Integration**: Install Julia and run with JuliaHub package server configuration
@@ -148,6 +149,12 @@ go build -o jh .
148149
- `jh dataset upload [dataset-id] <file-path>` - Upload a dataset
149150
- `jh dataset status <dataset-id> [version]` - Show dataset status
150151

152+
### Registry Management (`jh registry`)
153+
154+
- `jh registry list` - List all package registries on JuliaHub
155+
- Default: Shows only UUID and Name
156+
- `jh registry list --verbose` - Show detailed registry information including owner, creation date, package count, and description
157+
151158
### Project Management (`jh project`)
152159

153160
- `jh project list` - List all accessible projects
@@ -214,6 +221,19 @@ jh dataset upload --new ./my-data.tar.gz
214221
jh dataset upload my-dataset ./updated-data.tar.gz
215222
```
216223

224+
### Registry Operations
225+
226+
```bash
227+
# List all registries (UUID and Name only)
228+
jh registry list
229+
230+
# List registries with detailed information
231+
jh registry list --verbose
232+
233+
# List registries on custom server
234+
jh registry list -s yourinstall
235+
```
236+
217237
### Project Operations
218238

219239
```bash

main.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,12 @@ job execution, project management, Git integration, and package hosting capabili
161161
Available command categories:
162162
auth - Authentication and token management
163163
dataset - Dataset operations (list, download, upload, status)
164+
registry - Registry management (list registries)
164165
project - Project management (list, filter by user)
165166
user - User information and profile
166167
clone - Clone projects with automatic authentication
167168
push - Push changes with authentication
168-
fetch - Fetch updates with authentication
169+
fetch - Fetch updates with authentication
169170
pull - Pull changes with authentication
170171
julia - Julia installation and management
171172
run - Run Julia with JuliaHub configuration
@@ -550,6 +551,47 @@ Displays:
550551
},
551552
}
552553

554+
var registryCmd = &cobra.Command{
555+
Use: "registry",
556+
Short: "Registry management commands",
557+
Long: `Manage Julia package registries on JuliaHub.
558+
559+
Registries are collections of Julia packages that can be registered and
560+
installed. JuliaHub supports multiple registries including the General
561+
registry, custom organizational registries, and test registries.`,
562+
}
563+
564+
var registryListCmd = &cobra.Command{
565+
Use: "list",
566+
Short: "List registries",
567+
Long: `List all package registries on JuliaHub.
568+
569+
By default, displays only UUID and Name for each registry.
570+
Use --verbose flag to display comprehensive information including:
571+
- Registry UUID
572+
- Registry name and ID
573+
- Owner information
574+
- Creation date
575+
- Package count
576+
- Description
577+
- Registration status`,
578+
Example: " jh registry list\n jh registry list --verbose\n jh registry list -s custom-server.com",
579+
Run: func(cmd *cobra.Command, args []string) {
580+
server, err := getServerFromFlagOrConfig(cmd)
581+
if err != nil {
582+
fmt.Printf("Failed to get server config: %v\n", err)
583+
os.Exit(1)
584+
}
585+
586+
verbose, _ := cmd.Flags().GetBool("verbose")
587+
588+
if err := listRegistries(server, verbose); err != nil {
589+
fmt.Printf("Failed to list registries: %v\n", err)
590+
os.Exit(1)
591+
}
592+
},
593+
}
594+
553595
var projectCmd = &cobra.Command{
554596
Use: "project",
555597
Short: "Project management commands",
@@ -982,6 +1024,8 @@ func init() {
9821024
datasetUploadCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
9831025
datasetUploadCmd.Flags().Bool("new", false, "Create a new dataset")
9841026
datasetStatusCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
1027+
registryListCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
1028+
registryListCmd.Flags().Bool("verbose", false, "Show detailed registry information")
9851029
projectListCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
9861030
projectListCmd.Flags().String("user", "", "Filter projects by user (leave empty to show only your own projects)")
9871031
userInfoCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
@@ -994,13 +1038,14 @@ func init() {
9941038
authCmd.AddCommand(authLoginCmd, authRefreshCmd, authStatusCmd, authEnvCmd)
9951039
jobCmd.AddCommand(jobListCmd, jobStartCmd)
9961040
datasetCmd.AddCommand(datasetListCmd, datasetDownloadCmd, datasetUploadCmd, datasetStatusCmd)
1041+
registryCmd.AddCommand(registryListCmd)
9971042
projectCmd.AddCommand(projectListCmd)
9981043
userCmd.AddCommand(userInfoCmd)
9991044
juliaCmd.AddCommand(juliaInstallCmd)
10001045
runCmd.AddCommand(runSetupCmd)
10011046
gitCredentialCmd.AddCommand(gitCredentialHelperCmd, gitCredentialGetCmd, gitCredentialStoreCmd, gitCredentialEraseCmd, gitCredentialSetupCmd)
10021047

1003-
rootCmd.AddCommand(authCmd, jobCmd, datasetCmd, projectCmd, userCmd, juliaCmd, cloneCmd, pushCmd, fetchCmd, pullCmd, runCmd, gitCredentialCmd, updateCmd)
1048+
rootCmd.AddCommand(authCmd, jobCmd, datasetCmd, registryCmd, projectCmd, userCmd, juliaCmd, cloneCmd, pushCmd, fetchCmd, pullCmd, runCmd, gitCredentialCmd, updateCmd)
10041049
}
10051050

10061051
func main() {

registries.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"time"
9+
)
10+
11+
type Registry struct {
12+
UUID string `json:"uuid"`
13+
Name string `json:"name"`
14+
RegistryID int `json:"registry_id"`
15+
Owner *string `json:"owner"`
16+
Register bool `json:"register"`
17+
CreationDate CustomTime `json:"creation_date"`
18+
PackageCount int `json:"package_count"`
19+
Description string `json:"description"`
20+
}
21+
22+
func listRegistries(server string, verbose bool) error {
23+
token, err := ensureValidToken()
24+
if err != nil {
25+
return fmt.Errorf("authentication required: %w", err)
26+
}
27+
28+
url := fmt.Sprintf("https://%s/api/v1/registry/registries/descriptions", server)
29+
30+
req, err := http.NewRequest("GET", url, nil)
31+
if err != nil {
32+
return fmt.Errorf("failed to create request: %w", err)
33+
}
34+
35+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.IDToken))
36+
req.Header.Set("Accept", "application/json")
37+
38+
client := &http.Client{Timeout: 30 * time.Second}
39+
resp, err := client.Do(req)
40+
if err != nil {
41+
return fmt.Errorf("failed to make request: %w", err)
42+
}
43+
defer resp.Body.Close()
44+
45+
if resp.StatusCode != http.StatusOK {
46+
body, _ := io.ReadAll(resp.Body)
47+
return fmt.Errorf("API request failed (status %d): %s", resp.StatusCode, string(body))
48+
}
49+
50+
body, err := io.ReadAll(resp.Body)
51+
if err != nil {
52+
return fmt.Errorf("failed to read response: %w", err)
53+
}
54+
55+
var registries []Registry
56+
if err := json.Unmarshal(body, &registries); err != nil {
57+
return fmt.Errorf("failed to parse response: %w", err)
58+
}
59+
60+
if len(registries) == 0 {
61+
fmt.Println("No registries found")
62+
return nil
63+
}
64+
65+
fmt.Printf("Found %d registr%s:\n\n", len(registries), pluralize(len(registries), "y", "ies"))
66+
67+
if verbose {
68+
// Verbose mode: show all details
69+
for _, registry := range registries {
70+
fmt.Printf("UUID: %s\n", registry.UUID)
71+
fmt.Printf("Name: %s\n", registry.Name)
72+
if registry.Owner != nil {
73+
fmt.Printf("Owner: %s\n", *registry.Owner)
74+
} else {
75+
fmt.Printf("Owner: (none)\n")
76+
}
77+
fmt.Printf("Register: %t\n", registry.Register)
78+
fmt.Printf("Creation Date: %s\n", registry.CreationDate.Time.Format(time.RFC3339))
79+
fmt.Printf("Package Count: %d\n", registry.PackageCount)
80+
if registry.Description != "" {
81+
fmt.Printf("Description: %s\n", registry.Description)
82+
}
83+
fmt.Println()
84+
}
85+
} else {
86+
// Default mode: show only UUID and Name
87+
for _, registry := range registries {
88+
fmt.Printf("UUID: %s\n", registry.UUID)
89+
fmt.Printf("Name: %s\n", registry.Name)
90+
fmt.Println()
91+
}
92+
}
93+
94+
return nil
95+
}
96+
97+
func pluralize(count int, singular, plural string) string {
98+
if count == 1 {
99+
return singular
100+
}
101+
return plural
102+
}

0 commit comments

Comments
 (0)