Skip to content

Commit 9fd2cee

Browse files
updated documentation
1 parent 2a07770 commit 9fd2cee

File tree

4 files changed

+159
-4
lines changed

4 files changed

+159
-4
lines changed

CLAUDE.md

Lines changed: 9 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)
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,11 @@ 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+
```
93+
8794
### Test project and user operations
8895
```bash
8996
go run . project list

README.md

Lines changed: 15 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,10 @@ 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+
151156
### Project Management (`jh project`)
152157

153158
- `jh project list` - List all accessible projects
@@ -214,6 +219,16 @@ jh dataset upload --new ./my-data.tar.gz
214219
jh dataset upload my-dataset ./updated-data.tar.gz
215220
```
216221

222+
### Registry Operations
223+
224+
```bash
225+
# List all registries
226+
jh registry list
227+
228+
# List registries on custom server
229+
jh registry list -s yourinstall
230+
```
231+
217232
### Project Operations
218233

219234
```bash

main.go

Lines changed: 43 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,44 @@ 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+
Displays information including:
570+
- Registry UUID
571+
- Registry name and ID
572+
- Owner information
573+
- Creation date
574+
- Package count
575+
- Description
576+
- Registration status`,
577+
Example: " jh registry list\n jh registry list -s custom-server.com",
578+
Run: func(cmd *cobra.Command, args []string) {
579+
server, err := getServerFromFlagOrConfig(cmd)
580+
if err != nil {
581+
fmt.Printf("Failed to get server config: %v\n", err)
582+
os.Exit(1)
583+
}
584+
585+
if err := listRegistries(server); err != nil {
586+
fmt.Printf("Failed to list registries: %v\n", err)
587+
os.Exit(1)
588+
}
589+
},
590+
}
591+
553592
var projectCmd = &cobra.Command{
554593
Use: "project",
555594
Short: "Project management commands",
@@ -982,6 +1021,7 @@ func init() {
9821021
datasetUploadCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
9831022
datasetUploadCmd.Flags().Bool("new", false, "Create a new dataset")
9841023
datasetStatusCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
1024+
registryListCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
9851025
projectListCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
9861026
projectListCmd.Flags().String("user", "", "Filter projects by user (leave empty to show only your own projects)")
9871027
userInfoCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
@@ -994,13 +1034,14 @@ func init() {
9941034
authCmd.AddCommand(authLoginCmd, authRefreshCmd, authStatusCmd, authEnvCmd)
9951035
jobCmd.AddCommand(jobListCmd, jobStartCmd)
9961036
datasetCmd.AddCommand(datasetListCmd, datasetDownloadCmd, datasetUploadCmd, datasetStatusCmd)
1037+
registryCmd.AddCommand(registryListCmd)
9971038
projectCmd.AddCommand(projectListCmd)
9981039
userCmd.AddCommand(userInfoCmd)
9991040
juliaCmd.AddCommand(juliaInstallCmd)
10001041
runCmd.AddCommand(runSetupCmd)
10011042
gitCredentialCmd.AddCommand(gitCredentialHelperCmd, gitCredentialGetCmd, gitCredentialStoreCmd, gitCredentialEraseCmd, gitCredentialSetupCmd)
10021043

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

10061047
func main() {

registries.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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) 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/ui/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+
for _, registry := range registries {
67+
fmt.Printf("UUID: %s\n", registry.UUID)
68+
fmt.Printf("Name: %s\n", registry.Name)
69+
fmt.Printf("Registry ID: %d\n", registry.RegistryID)
70+
if registry.Owner != nil {
71+
fmt.Printf("Owner: %s\n", *registry.Owner)
72+
} else {
73+
fmt.Printf("Owner: (none)\n")
74+
}
75+
fmt.Printf("Register: %t\n", registry.Register)
76+
fmt.Printf("Creation Date: %s\n", registry.CreationDate.Time.Format(time.RFC3339))
77+
fmt.Printf("Package Count: %d\n", registry.PackageCount)
78+
if registry.Description != "" {
79+
fmt.Printf("Description: %s\n", registry.Description)
80+
}
81+
fmt.Println()
82+
}
83+
84+
return nil
85+
}
86+
87+
func pluralize(count int, singular, plural string) string {
88+
if count == 1 {
89+
return singular
90+
}
91+
return plural
92+
}

0 commit comments

Comments
 (0)