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
13 changes: 11 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

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.
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.

## Architecture

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

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

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

### Test registry operations
```bash
go run . registry list
go run . registry list --verbose
```

### Test project and user operations
```bash
go run . project list
Expand Down Expand Up @@ -273,6 +281,7 @@ jh run setup
- Clone command automatically resolves `username/project` format to project UUIDs
- Folder naming conflicts are resolved with automatic numbering (project-1, project-2, etc.)
- Credential helper follows Git protocol: responds only to JuliaHub URLs, ignores others
- Registry list output is concise by default (UUID and Name only); use `--verbose` flag for detailed information (owner, creation date, package count, description)

## Implementation Details

Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ A command-line interface for interacting with JuliaHub, a platform for Julia com

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

### Registry Management (`jh registry`)

- `jh registry list` - List all package registries on JuliaHub
- Default: Shows only UUID and Name
- `jh registry list --verbose` - Show detailed registry information including owner, creation date, package count, and description

### Project Management (`jh project`)

- `jh project list` - List all accessible projects
Expand Down Expand Up @@ -214,6 +221,19 @@ jh dataset upload --new ./my-data.tar.gz
jh dataset upload my-dataset ./updated-data.tar.gz
```

### Registry Operations

```bash
# List all registries (UUID and Name only)
jh registry list

# List registries with detailed information
jh registry list --verbose

# List registries on custom server
jh registry list -s yourinstall
```

### Project Operations

```bash
Expand Down
174 changes: 172 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,13 @@ job execution, project management, Git integration, and package hosting capabili
Available command categories:
auth - Authentication and token management
dataset - Dataset operations (list, download, upload, status)
package - Package search and exploration
registry - Registry management (list registries)
project - Project management (list, filter by user)
user - User information and profile
clone - Clone projects with automatic authentication
push - Push changes with authentication
fetch - Fetch updates with authentication
fetch - Fetch updates with authentication
pull - Pull changes with authentication
julia - Julia installation and management
run - Run Julia with JuliaHub configuration
Expand Down Expand Up @@ -550,6 +552,162 @@ Displays:
},
}

var packageCmd = &cobra.Command{
Use: "package",
Short: "Package search commands",
Long: `Search and explore Julia packages on JuliaHub.

Packages are Julia libraries that provide reusable functionality. JuliaHub
hosts packages from multiple registries and provides comprehensive search
capabilities including filtering by tags, installation status, failures, and more.`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets remove installation status and failure since they either dont work or should be removed

}

var packageSearchCmd = &cobra.Command{
Use: "search [search-term]",
Short: "Search for packages",
Long: `Search for Julia packages on JuliaHub.

Displays package information including:
- Package name, owner, and UUID
- Version information
- Description and repository
- Tags and star count
- Installation status
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets remove installation status

- License information

Filtering options:
- Filter by registry using --registries flag (searches all registries by default)
- Filter by installation status (--installed, --not-installed)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above lets remove these

- Filter by packages with download failures (--has-failures)

Use --verbose flag for comprehensive output, or get a concise summary by default.`,
Example: " jh package search dataframes\n jh package search --installed\n jh package search --verbose plots\n jh package search --limit 20 ml\n jh package search --registries General optimization",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
server, err := getServerFromFlagOrConfig(cmd)
if err != nil {
fmt.Printf("Failed to get server config: %v\n", err)
os.Exit(1)
}

search := ""
if len(args) > 0 {
search = args[0]
}

limit, _ := cmd.Flags().GetInt("limit")
offset, _ := cmd.Flags().GetInt("offset")
verbose, _ := cmd.Flags().GetBool("verbose")
registryNamesStr, _ := cmd.Flags().GetString("registries")

// Handle boolean flags - only set if explicitly provided
var installed *bool
var notInstalled *bool
var hasFailures *bool

if cmd.Flags().Changed("installed") {
val, _ := cmd.Flags().GetBool("installed")
installed = &val
}

if cmd.Flags().Changed("not-installed") {
val, _ := cmd.Flags().GetBool("not-installed")
notInstalled = &val
}

if cmd.Flags().Changed("has-failures") {
val, _ := cmd.Flags().GetBool("has-failures")
hasFailures = &val
}

// Fetch all registries from the API
allRegistries, err := fetchRegistries(server)
if err != nil {
fmt.Printf("Failed to fetch registries: %v\n", err)
os.Exit(1)
}

// Determine which registry IDs to use
var registryIDs []int
if registryNamesStr != "" {
// Use only specified registries
requestedNames := strings.Split(registryNamesStr, ",")
for _, requestedName := range requestedNames {
requestedName = strings.TrimSpace(requestedName)
if requestedName == "" {
continue
}

// Find matching registry (case-insensitive)
found := false
for _, reg := range allRegistries {
if strings.EqualFold(reg.Name, requestedName) {
registryIDs = append(registryIDs, reg.RegistryID)
found = true
break
}
}

if !found {
fmt.Printf("Registry not found: '%s'\n", requestedName)
os.Exit(1)
}
}
} else {
// Use all registries
for _, reg := range allRegistries {
registryIDs = append(registryIDs, reg.RegistryID)
}
}

if err := searchPackages(server, search, limit, offset, installed, notInstalled, hasFailures, registryIDs, verbose); err != nil {
fmt.Printf("Failed to search packages: %v\n", err)
os.Exit(1)
}
},
}

var registryCmd = &cobra.Command{
Use: "registry",
Short: "Registry management commands",
Long: `Manage Julia package registries on JuliaHub.

Registries are collections of Julia packages that can be registered and
installed. JuliaHub supports multiple registries including the General
registry, custom organizational registries, and test registries.`,
}

var registryListCmd = &cobra.Command{
Use: "list",
Short: "List registries",
Long: `List all package registries on JuliaHub.

By default, displays, UUID, and Name for each registry.
Use --verbose flag to display comprehensive information including:
- Registry UUID
- Registry name and ID
- Owner information
- Creation date
- Package count
- Description
- Registration status`,
Example: " jh registry list\n jh registry list --verbose\n jh registry list -s custom-server.com",
Run: func(cmd *cobra.Command, args []string) {
server, err := getServerFromFlagOrConfig(cmd)
if err != nil {
fmt.Printf("Failed to get server config: %v\n", err)
os.Exit(1)
}

verbose, _ := cmd.Flags().GetBool("verbose")

if err := listRegistries(server, verbose); err != nil {
fmt.Printf("Failed to list registries: %v\n", err)
os.Exit(1)
}
},
}

var projectCmd = &cobra.Command{
Use: "project",
Short: "Project management commands",
Expand Down Expand Up @@ -982,6 +1140,16 @@ func init() {
datasetUploadCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
datasetUploadCmd.Flags().Bool("new", false, "Create a new dataset")
datasetStatusCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
packageSearchCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
packageSearchCmd.Flags().Int("limit", 10, "Maximum number of results to return")
packageSearchCmd.Flags().Int("offset", 0, "Number of results to skip")
packageSearchCmd.Flags().Bool("installed", false, "Filter by installed packages")
packageSearchCmd.Flags().Bool("not-installed", false, "Filter by not installed packages")
packageSearchCmd.Flags().Bool("has-failures", false, "Filter by packages with download failures")
packageSearchCmd.Flags().String("registries", "", "Filter by registry names (comma-separated, e.g., 'General,CustomRegistry')")
packageSearchCmd.Flags().Bool("verbose", false, "Show detailed package information")
registryListCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
registryListCmd.Flags().Bool("verbose", false, "Show detailed registry information")
projectListCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
projectListCmd.Flags().String("user", "", "Filter projects by user (leave empty to show only your own projects)")
userInfoCmd.Flags().StringP("server", "s", "juliahub.com", "JuliaHub server")
Expand All @@ -994,13 +1162,15 @@ func init() {
authCmd.AddCommand(authLoginCmd, authRefreshCmd, authStatusCmd, authEnvCmd)
jobCmd.AddCommand(jobListCmd, jobStartCmd)
datasetCmd.AddCommand(datasetListCmd, datasetDownloadCmd, datasetUploadCmd, datasetStatusCmd)
packageCmd.AddCommand(packageSearchCmd)
registryCmd.AddCommand(registryListCmd)
projectCmd.AddCommand(projectListCmd)
userCmd.AddCommand(userInfoCmd)
juliaCmd.AddCommand(juliaInstallCmd)
runCmd.AddCommand(runSetupCmd)
gitCredentialCmd.AddCommand(gitCredentialHelperCmd, gitCredentialGetCmd, gitCredentialStoreCmd, gitCredentialEraseCmd, gitCredentialSetupCmd)

rootCmd.AddCommand(authCmd, jobCmd, datasetCmd, projectCmd, userCmd, juliaCmd, cloneCmd, pushCmd, fetchCmd, pullCmd, runCmd, gitCredentialCmd, updateCmd)
rootCmd.AddCommand(authCmd, jobCmd, datasetCmd, packageCmd, registryCmd, projectCmd, userCmd, juliaCmd, cloneCmd, pushCmd, fetchCmd, pullCmd, runCmd, gitCredentialCmd, updateCmd)
}

func main() {
Expand Down
62 changes: 62 additions & 0 deletions package_search.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
query FilteredPackages(
$search: String
$limit: Int
$offset: Int
$matchtags: _text
$registries: _int8
$hasfailures: Boolean
$installed: Boolean
$notinstalled: Boolean
$licenses: _text
$order: [package_rank_vw_order_by!]
$filter: package_rank_vw_bool_exp = {}
) {
package_search(
args: {
search: $search
matchtags: $matchtags
licenses: $licenses
isinstalled: $installed
notinstalled: $notinstalled
hasfailures: $hasfailures
registrylist: $registries
}
order_by: $order
limit: $limit
offset: $offset
where: { _and: [{ fit: { _gte: 1 } }, $filter] }
) {
name
owner
slug
license
isapp
score
registrymap {
version
registryid
status
isapp
isjsml
__typename
}
metadata {
docshosteduri
versions
description
docslink
repo
owner
tags
starcount
__typename
}
uuid
installed
failures {
package_version
__typename
}
__typename
}
}
Loading