440 lines
12 KiB
Plaintext
440 lines
12 KiB
Plaintext
# 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)
|
|
}
|
|
}
|