# OCI CLI Commands # User-facing commands for OCI artifact management # Version: 1.0.0 use ../config/loader.nu get-config use ./client.nu * use std log # Pull OCI artifact to local cache export def "oci pull" [ artifact: string # Format: name:version or registry/namespace/name:version --registry: string # Override default registry --namespace: string = "provisioning-extensions" # Override default namespace --destination: string # Local path (default: workspace extensions) --insecure # Skip TLS verification ] -> nothing { let config = (get-config) # Parse artifact reference let parts = ($artifact | split row ":") let artifact_name = ($parts | get 0) let version = ($parts | get 1? | default "latest") # Determine registry let reg = if ($registry | is-not-empty) { $registry } else if ($config.registry? | is-not-empty) { $config.registry.oci.endpoint } else { "localhost:5000" } # Determine destination let dest = if ($destination | is-not-empty) { $destination } else { let ws_extensions = ($config.paths.extensions | path join $artifact_name) mkdir $ws_extensions $ws_extensions } # Get authentication token if configured let auth_token = if ($config.registry?.oci?.auth_token_path? | is-not-empty) { open ($config.registry.oci.auth_token_path | path expand) } else { "" } print $"Pulling ($reg)/($namespace)/($artifact_name):($version)..." let result = (pull-artifact $reg $namespace $artifact_name $version $dest --auth-token $auth_token --insecure=$insecure) if $result { print $"✓ Successfully pulled ($artifact_name):($version) to ($dest)" } else { error make { msg: $"Failed to pull ($artifact_name):($version)" } } } # Push local extension as OCI artifact export def "oci push" [ source_path: string # Local extension directory artifact_name: string # Artifact name version: string # Artifact version --registry: string # Override default registry --namespace: string = "provisioning-extensions" --insecure ] -> nothing { let config = (get-config) # Validate source exists if not ($source_path | path exists) { error make { msg: $"Source path not found: ($source_path)" } } # Determine registry let reg = if ($registry | is-not-empty) { $registry } else if ($config.registry? | is-not-empty) { $config.registry.oci.endpoint } else { "localhost:5000" } # Get authentication token let auth_token = if ($config.registry?.oci?.auth_token_path? | is-not-empty) { open ($config.registry.oci.auth_token_path | path expand) } else { "" } print $"Pushing ($artifact_name):($version) to ($reg)/($namespace)..." let result = (push-artifact $source_path $reg $namespace $artifact_name $version --auth-token $auth_token --insecure=$insecure) if $result { print $"✓ Successfully pushed ($artifact_name):($version)" print $" Reference: ($reg)/($namespace)/($artifact_name):($version)" } else { error make { msg: $"Failed to push ($artifact_name):($version)" } } } # List available artifacts in registry export def "oci list" [ --namespace: string = "provisioning-extensions" --registry: string --insecure ] -> table { let config = (get-config) let reg = if ($registry | is-not-empty) { $registry } else if ($config.registry? | is-not-empty) { $config.registry.oci.endpoint } else { "localhost:5000" } let auth_token = if ($config.registry?.oci?.auth_token_path? | is-not-empty) { open ($config.registry.oci.auth_token_path | path expand) } else { "" } print $"Listing artifacts in ($reg)/($namespace)..." let artifacts = (list-artifacts $reg $namespace --auth-token $auth_token --insecure=$insecure) if ($artifacts | is-empty) { print "No artifacts found" return [] } # Format as table $artifacts | each { |artifact| { name: $artifact registry: $reg namespace: $namespace reference: $"($reg)/($namespace)/($artifact)" } } } # Search for artifacts matching query export def "oci search" [ query: string # Search query (artifact name pattern) --namespace: string = "provisioning-extensions" --registry: string --insecure ] -> table { let artifacts = (oci list --namespace $namespace --registry $registry --insecure=$insecure) $artifacts | where name =~ $query } # Show artifact tags (versions) export def "oci tags" [ artifact_name: string # Artifact name --namespace: string = "provisioning-extensions" --registry: string --insecure ] -> table { let config = (get-config) let reg = if ($registry | is-not-empty) { $registry } else if ($config.registry? | is-not-empty) { $config.registry.oci.endpoint } else { "localhost:5000" } let auth_token = if ($config.registry?.oci?.auth_token_path? | is-not-empty) { open ($config.registry.oci.auth_token_path | path expand) } else { "" } print $"Fetching tags for ($artifact_name)..." let tags = (get-artifact-tags $reg $namespace $artifact_name --auth-token $auth_token --insecure=$insecure) if ($tags | is-empty) { print $"No tags found for ($artifact_name)" return [] } # Format as table with semantic version sorting $tags | each { |tag| { artifact: $artifact_name version: $tag reference: $"($reg)/($namespace)/($artifact_name):($tag)" } } | sort-by version --reverse } # Inspect artifact manifest export def "oci inspect" [ artifact: string # Format: name:version --namespace: string = "provisioning-extensions" --registry: string --insecure --format: string = "yaml" # Output format: yaml, json ] -> nothing { let config = (get-config) # Parse artifact reference let parts = ($artifact | split row ":") let artifact_name = ($parts | get 0) let version = ($parts | get 1? | default "latest") let reg = if ($registry | is-not-empty) { $registry } else if ($config.registry? | is-not-empty) { $config.registry.oci.endpoint } else { "localhost:5000" } let auth_token = if ($config.registry?.oci?.auth_token_path? | is-not-empty) { open ($config.registry.oci.auth_token_path | path expand) } else { "" } print $"Inspecting ($reg)/($namespace)/($artifact_name):($version)..." let manifest = (get-artifact-manifest $reg $namespace $artifact_name $version --auth-token $auth_token --insecure=$insecure) if ($manifest | is-empty) { error make { msg: $"Artifact not found or manifest unavailable" } } # Output in requested format match $format { "json" => { $manifest | to json } "yaml" => { $manifest | to yaml } _ => { $manifest } } | print } # Login to OCI registry export def "oci login" [ registry: string # Registry endpoint --username: string = "_token" # Username (default: _token) --password-stdin # Read password from stdin --token-file: string # Path to token file ] -> nothing { let password = if $password_stdin { input --suppress-output "Password: " } else if ($token_file | is-not-empty) { open ($token_file | path expand) } else { input --suppress-output "Password: " } # Use docker login for credential storage try { print $"Logging in to ($registry)..." echo $password | docker login $registry --username $username --password-stdin print $"✓ Successfully logged in to ($registry)" print $" Credentials stored in docker config" } catch { error make { msg: $"Failed to login to ($registry)" } } } # Logout from OCI registry export def "oci logout" [ registry: string # Registry endpoint ] -> nothing { try { docker logout $registry print $"✓ Logged out from ($registry)" } catch { error make { msg: $"Failed to logout from ($registry)" } } } # Delete artifact from registry export def "oci delete" [ artifact: string # Format: name:version --namespace: string = "provisioning-extensions" --registry: string --insecure --force # Skip confirmation ] -> nothing { let config = (get-config) # Parse artifact reference let parts = ($artifact | split row ":") let artifact_name = ($parts | get 0) let version = ($parts | get 1? | default "latest") let reg = if ($registry | is-not-empty) { $registry } else if ($config.registry? | is-not-empty) { $config.registry.oci.endpoint } else { "localhost:5000" } let full_ref = $"($reg)/($namespace)/($artifact_name):($version)" # Confirm deletion unless --force if not $force { let confirm = (input $"Delete ($full_ref)? (y/N): ") if ($confirm | str downcase) != "y" { print "Deletion cancelled" return } } let auth_token = if ($config.registry?.oci?.auth_token_path? | is-not-empty) { open ($config.registry.oci.auth_token_path | path expand) } else { "" } print $"Deleting ($full_ref)..." let result = (delete-artifact $reg $namespace $artifact_name $version --auth-token $auth_token --insecure=$insecure) if $result { print $"✓ Successfully deleted ($full_ref)" } else { error make { msg: $"Failed to delete ($full_ref)" } } } # Copy artifact between registries export def "oci copy" [ source: string # Format: [registry/namespace/]name:version destination: string # Format: [registry/namespace/]name:version --insecure ] -> nothing { # Parse source let src_parts = ($source | split row "/") let src_registry = if ($src_parts | length) > 2 { $src_parts | get 0 } else { "localhost:5000" } let src_namespace = if ($src_parts | length) > 2 { $src_parts | get 1 } else { "provisioning-extensions" } let src_artifact = if ($src_parts | length) > 2 { $src_parts | get 2 } else { $src_parts | get 0 } let src_name_ver = ($src_artifact | split row ":") let src_name = ($src_name_ver | get 0) let src_version = ($src_name_ver | get 1? | default "latest") # Parse destination let dest_parts = ($destination | split row "/") let dest_registry = if ($dest_parts | length) > 2 { $dest_parts | get 0 } else { "localhost:5000" } let dest_namespace = if ($dest_parts | length) > 2 { $dest_parts | get 1 } else { "provisioning-extensions" } let dest_artifact = if ($dest_parts | length) > 2 { $dest_parts | get 2 } else { $dest_parts | get 0 } let dest_name_ver = ($dest_artifact | split row ":") let dest_name = ($dest_name_ver | get 0) let dest_version = ($dest_name_ver | get 1? | default $src_version) print $"Copying ($src_registry)/($src_namespace)/($src_name):($src_version)" print $" to ($dest_registry)/($dest_namespace)/($dest_name):($dest_version)" let result = (copy-artifact $src_registry $src_namespace $dest_registry $dest_namespace $src_name $src_version --insecure=$insecure) if $result { print $"✓ Successfully copied artifact" } else { error make { msg: "Failed to copy artifact" } } } # Show OCI configuration export def "oci config" [] -> record { let config = (get-config) { tool: (detect-oci-tool) registry: ($config.registry?.oci?.endpoint? | default "localhost:5000") namespace: ($config.registry?.oci?.namespaces? | default {}) cache_dir: ($env.HOME | path join ".provisioning" "oci-cache") tls_enabled: ($config.registry?.oci?.tls_enabled? | default false) } }