440 lines
12 KiB
Plaintext
Raw Normal View History

2025-10-07 10:32:04 +01:00
# 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)
}
}