# Extension Discovery and Search # Discovers extensions across OCI registries, Gitea, and local sources use ../utils/logging.nu * use ../oci/client.nu * use versions.nu [is-semver, sort-by-semver, get-latest-version] # Discover extensions in OCI registry export def discover-oci-extensions [ oci_config?: record extension_type?: string ]: nothing -> list { let result = (do { let config = if ($oci_config | is-empty) { get-oci-config } else { $oci_config } let token = (load-oci-token $config.auth_token_path) log-info $"Discovering extensions in OCI registry: ($config.registry)/($config.namespace)" # List all artifacts let artifacts = (oci-list-artifacts $config.registry $config.namespace --auth-token $token) if ($artifacts | is-empty) { log-warn "No artifacts found in OCI registry" return [] } # Get metadata for each artifact let extensions = ($artifacts | each {|artifact_name| let item_result = (do { let tags = (oci-get-artifact-tags $config.registry $config.namespace $artifact_name --auth-token $token) if ($tags | is-empty) { null } else { let semver_tags = ($tags | where ($it | is-semver)) let latest = if ($semver_tags | is-not-empty) { $semver_tags | sort-by-semver | last } else { $tags | last } # Get manifest for latest version let manifest = (oci-get-artifact-manifest $config.registry $config.namespace $artifact_name $latest --auth-token $token ) # Extract extension type from annotations let ext_type = (extract-extension-type $manifest) { name: $artifact_name type: $ext_type versions: $tags latest: $latest source: "oci" registry: $config.registry namespace: $config.namespace digest: ($manifest.config?.digest? | default "") annotations: ($manifest.config?.annotations? | default {}) } } } | complete) if $item_result.exit_code == 0 { $item_result.stdout } else { log-warn $"Failed to get metadata for ($artifact_name): ($item_result.stderr)" null } } | compact) # Filter by extension type if specified if ($extension_type | is-not-empty) { $extensions | where type == $extension_type } else { $extensions } } | complete) if $result.exit_code == 0 { $result.stdout } else { log-error $"Failed to discover OCI extensions: ($result.stderr)" [] } } # Search extensions in OCI registry export def search-oci-extensions [ query: string oci_config?: record ]: nothing -> list { let result = (do { let all_extensions = (discover-oci-extensions $oci_config) $all_extensions | where {|ext| ($ext.name | str contains $query) or ($ext.type | str contains $query) } } | complete) if $result.exit_code == 0 { $result.stdout } else { log-error $"Failed to search OCI extensions: ($result.stderr)" [] } } # Get extension metadata from OCI registry export def get-oci-extension-metadata [ extension_name: string version: string oci_config?: record ]: nothing -> record { let result = (do { let config = if ($oci_config | is-empty) { get-oci-config } else { $oci_config } let token = (load-oci-token $config.auth_token_path) let manifest = (oci-get-artifact-manifest $config.registry $config.namespace $extension_name $version --auth-token $token ) if ($manifest | is-empty) { return {} } { name: $extension_name version: $version source: "oci" registry: $config.registry namespace: $config.namespace oci_digest: ($manifest.config?.digest? | default "") created: ($manifest.config?.created? | default "") size: ($manifest.config?.size? | default 0) annotations: ($manifest.config?.annotations? | default {}) layers: ($manifest.layers? | default []) media_type: ($manifest.mediaType? | default "") } } | complete) if $result.exit_code == 0 { $result.stdout } else { log-error $"Failed to get OCI extension metadata: ($result.stderr)" {} } } # Discover local extensions export def discover-local-extensions [ extension_type?: string ]: nothing -> list { let extension_paths = [ ($env.PWD | path join ".provisioning" "extensions") ($env.HOME | path join ".provisioning-extensions") "/opt/provisioning-extensions" ] | where ($it | path exists) let extensions = ($extension_paths | each {|base_path| discover-in-path $base_path $extension_type } | flatten) $extensions } # Discover extensions in specific path def discover-in-path [ base_path: string extension_type?: string ]: nothing -> list { let type_dirs = if ($extension_type | is-not-empty) { [$extension_type] } else { ["providers", "taskservs", "clusters"] } $type_dirs | each {|type_dir| let type_path = ($base_path | path join $type_dir) if not ($type_path | path exists) { return [] } let extensions = (ls $type_path | where type == dir | get name | each {|ext_path| let item_result = (do { let ext_name = ($ext_path | path basename) let manifest_file = ($ext_path | path join "extension.yaml") let manifest = if ($manifest_file | path exists) { open $manifest_file | from yaml } else { { extension: { name: $ext_name type: $type_dir version: "local" } } } { name: ($manifest.extension.name? | default $ext_name) type: ($manifest.extension.type? | default $type_dir) version: ($manifest.extension.version? | default "local") path: $ext_path source: "local" description: ($manifest.extension.description? | default "") } } | complete) if $item_result.exit_code == 0 { $item_result.stdout } else { log-warn $"Failed to read extension at ($ext_path): ($item_result.stderr)" null } } | compact ) $extensions } | flatten } # Discover all extensions (OCI, Gitea, Local) export def discover-all-extensions [ extension_type?: string --include-oci --include-gitea --include-local ]: nothing -> list { mut all_extensions = [] # Discover from OCI if flag set or if no flags set (default all) if $include_oci or (not $include_oci and not $include_gitea and not $include_local) { if (is-oci-available) { let oci_exts = if ($extension_type | is-not-empty) { discover-oci-extensions {} $extension_type } else { discover-oci-extensions } $all_extensions = ($all_extensions | append $oci_exts) } } # Discover from Gitea if flag set or default if $include_gitea or (not $include_oci and not $include_gitea and not $include_local) { if (is-gitea-available) { # TODO: Implement Gitea discovery log-debug "Gitea discovery not yet implemented" } } # Discover from local if flag set or default if $include_local or (not $include_oci and not $include_gitea and not $include_local) { let local_exts = (discover-local-extensions $extension_type) $all_extensions = ($all_extensions | append $local_exts) } $all_extensions } # Search all sources for extensions export def search-extensions [ query: string --source: string = "all" # all, oci, gitea, local ]: nothing -> list { match $source { "oci" => { search-oci-extensions $query } "gitea" => { # TODO: Implement Gitea search log-warn "Gitea search not yet implemented" [] } "local" => { let local_exts = (discover-local-extensions) $local_exts | where {|ext| ($ext.name | str contains $query) or ($ext.type | str contains $query) or ($ext.description? | default "" | str contains $query) } } "all" => { let all = (discover-all-extensions) $all | where {|ext| ($ext.name | str contains $query) or ($ext.type | str contains $query) } } _ => { log-error $"Unknown source: ($source)" [] } } } # List extensions with detailed information export def list-extensions [ --extension-type: string = "" --source: string = "all" --format: string = "table" # table, json, yaml ]: nothing -> any { let extensions = (discover-all-extensions $extension_type) let filtered = if $source != "all" { $extensions | where source == $source } else { $extensions } match $format { "json" => ($filtered | to json) "yaml" => ($filtered | to yaml) "table" => { $filtered | select name type version source | sort-by type name } _ => $filtered } } # Get extension versions from all sources export def get-extension-versions [ extension_name: string --source: string = "all" ]: nothing -> list { mut versions = [] # Get from OCI if $source == "all" or $source == "oci" { if (is-oci-available) { let config = (get-oci-config) let token = (load-oci-token $config.auth_token_path) let oci_tags = (oci-get-artifact-tags $config.registry $config.namespace $extension_name --auth-token $token ) let oci_versions = ($oci_tags | each {|tag| {version: $tag, source: "oci"} }) $versions = ($versions | append $oci_versions) } } # Get from Gitea if $source == "all" or $source == "gitea" { # TODO: Implement Gitea versions } # Get from local if $source == "all" or $source == "local" { let local_exts = (discover-local-extensions) let local_matches = ($local_exts | where name == $extension_name) let local_versions = ($local_matches | each {|ext| {version: ($ext.version? | default "local"), source: "local"} }) $versions = ($versions | append $local_versions) } $versions } # Extract extension type from OCI manifest annotations def extract-extension-type [manifest: record]: nothing -> string { let annotations = ($manifest.config?.annotations? | default {}) # Try standard annotation let ext_type = ($annotations | get -o "provisioning.extension.type") if ($ext_type | is-not-empty) { return $ext_type } # Try OCI image labels let labels = ($manifest.config?.config?.Labels? | default {}) let label_type = ($labels | get -o "provisioning.extension.type") if ($label_type | is-not-empty) { return $label_type } # Default to unknown "unknown" } # Check if Gitea is available def is-gitea-available []: nothing -> bool { # TODO: Implement Gitea availability check false }