# Extension Publishing to Gitea # # Publish extensions as Gitea releases with packaged artifacts # # Version: 1.0.0 # Selective imports (ADR-025 Phase 3 Layer 2). use domain/gitea/api_client.nu [ create-release create-repository get-gitea-config get-gitea-token get-release-by-tag get-repository list-releases upload-release-asset ] use domain/config/loader.nu [get-config] # Validate extension structure def validate-extension [ extension_path: string ] -> record { let ext_path = $extension_path | path expand if not ($ext_path | path exists) { error make { msg: $"Extension path does not exist: ($ext_path)" } } # Check for required files let has_nickel_mod = $"($ext_path)/nickel/nickel.mod" | path exists let has_main_file = ( ls $"($ext_path)/nickel/*.ncl" | where name !~ ".*test.*" | length ) > 0 if not $has_nickel_mod { error make { msg: "Extension missing nickel/nickel.mod" } } if not $has_main_file { error make { msg: "Extension missing main Nickel file" } } # Get extension type from path let path_parts = $ext_path | split row "/" let ext_type = if ($path_parts | any {|p| $p == "providers"}) { "provider" } else if ($path_parts | any {|p| $p == "taskservs"}) { "taskserv" } else if ($path_parts | any {|p| $p == "clusters"}) { "cluster" } else { "unknown" } # Get extension name let ext_name = $ext_path | path basename { valid: true extension_type: $ext_type extension_name: $ext_name path: $ext_path } } # Package extension def package-extension [ extension_path: string version: string output_dir: string ] -> string { let ext_path = $extension_path | path expand let ext_name = $ext_path | path basename let out_dir = $output_dir | path expand mkdir $out_dir let archive_name = $"($ext_name)-($version).tar.gz" let archive_path = $"($out_dir)/($archive_name)" # Create tar archive cd ($ext_path | path dirname) ^tar czf $archive_path $ext_name print $"✓ Packaged extension: ($archive_path)" $archive_path } # Generate release notes from CHANGELOG def generate-release-notes [ extension_path: string version: string ] -> string { let changelog_path = $"($extension_path)/CHANGELOG.md" if not ($changelog_path | path exists) { return $"Release ($version)" } # Try to extract version section from CHANGELOG let changelog = open $changelog_path # Simple extraction: look for version header let version_section = $changelog | lines | reduce -f {found: false, lines: []} {|line, acc| if ($line | str contains $"## ($version)") { {found: true, lines: []} } else if $acc.found and ($line | str starts-with "## ") { # Next version found, stop $acc } else if $acc.found { {found: true, lines: ($acc.lines | append $line)} } else { $acc } } if ($version_section.lines | length) > 0 { $version_section.lines | str join "\n" | str trim } else { $"Release ($version)" } } # Publish extension to Gitea releases export def publish-extension-to-gitea [ extension_path: string version: string --release-notes: string = "" --draft: bool = false --prerelease: bool = false --output-dir: string = "/tmp/provisioning-extensions" ] -> record { # Validate extension let validation = validate-extension $extension_path print $"✓ Extension validated: ($validation.extension_name) (($validation.extension_type))" # Get Gitea config let config = get-gitea-config let org = $config.repositories.organization let repo = $config.repositories.extensions_repo # Ensure extensions repository exists let result = (do { get-repository $org $repo } | complete) if $result.exit_code != 0 { print $"Creating extensions repository: ($org)/($repo)" create-repository $org $repo "Provisioning extensions" false } # Package extension let archive_path = package-extension $extension_path $version $output_dir # Generate release notes let notes = if ($release_notes | is-empty) { generate-release-notes $extension_path $version } else { $release_notes } # Create git tag (if extension has git repo) let git_dir = $"($extension_path)/.git" if ($git_dir | path exists) { cd $extension_path let tag_result = (do { ^git tag -a $"($validation.extension_name)-($version)" -m $"Release ($version)" ^git push --tags } | complete) if $tag_result.exit_code == 0 { print $"✓ Git tag created: ($validation.extension_name)-($version)" } else { print $"⚠️ Could not create git tag (may already exist)" } } # Create Gitea release let tag_name = $"($validation.extension_name)-($version)" let release_name = $"($validation.extension_name) ($version)" let release = create-release $org $repo $tag_name $release_name $notes $draft $prerelease print $"✓ Release created: ($release.tag_name)" # Upload package as release asset upload-release-asset $org $repo $release.id $archive_path print $"✓ Asset uploaded: ($archive_path | path basename)" # Generate metadata file let metadata = { extension_name: $validation.extension_name extension_type: $validation.extension_type version: $version release_id: $release.id tag_name: $tag_name published_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ") archive_name: ($archive_path | path basename) release_url: $release.html_url } let metadata_path = $"($output_dir)/($validation.extension_name)-($version)-metadata.json" $metadata | save -f $metadata_path print $"✓ Extension published successfully!" print $" Release URL: ($release.html_url)" $metadata } # List published extensions export def list-gitea-extensions [ --extension-type: string = "" ] -> list { let config = get-gitea-config let org = $config.repositories.organization let repo = $config.repositories.extensions_repo # Get all releases let releases = list-releases $org $repo $releases | each {|release| # Parse extension name and version from tag let tag_parts = $release.tag_name | split row "-" let ext_name = $tag_parts | first ($tag_parts | length | $in - 1) | str join "-" let ext_version = $tag_parts | last # Determine extension type from name let ext_type = if ($ext_name | str ends-with "_prov") { "provider" } else if ($ext_name | str starts-with "cluster_") { "cluster" } else { "taskserv" } { extension_name: $ext_name extension_type: $ext_type version: $ext_version tag_name: $release.tag_name release_name: $release.name published_at: $release.published_at draft: $release.draft prerelease: $release.prerelease url: $release.html_url assets: ($release.assets | length) } } | if ($extension_type | is-empty) { $in } else { filter {|ext| $ext.extension_type == $extension_type} } | sort-by published_at --reverse } # Download extension from Gitea release export def download-gitea-extension [ extension_name: string version: string destination: string ] -> bool { let config = get-gitea-config let token = get-gitea-token $config let org = $config.repositories.organization let repo = $config.repositories.extensions_repo # Get release let tag_name = $"($extension_name)-($version)" let release = get-release-by-tag $org $repo $tag_name # Find asset let archive_name = $"($extension_name)-($version).tar.gz" let asset = $release.assets | where name == $archive_name | first if ($asset | is-empty) { error make { msg: $"Asset not found: ($archive_name)" } } # Download asset let download_url = $asset.browser_download_url let dest_path = $"($destination)/($archive_name)" | path expand mkdir ($destination | path expand) # Download with authorization ^curl -L -H $"Authorization: token ($token)" -o $dest_path $download_url print $"✓ Downloaded: ($dest_path)" # Extract archive cd ($destination | path expand) ^tar xzf $archive_name print $"✓ Extracted: ($extension_name)" true } # Get extension metadata from Gitea export def get-gitea-extension-metadata [ extension_name: string version: string ] -> record { let config = get-gitea-config let org = $config.repositories.organization let repo = $config.repositories.extensions_repo # Get release let tag_name = $"($extension_name)-($version)" let release = get-release-by-tag $org $repo $tag_name # Determine extension type let ext_type = if ($extension_name | str ends-with "_prov") { "provider" } else if ($extension_name | str starts-with "cluster_") { "cluster" } else { "taskserv" } { extension_name: $extension_name extension_type: $ext_type version: $version tag_name: $release.tag_name release_name: $release.name body: $release.body published_at: $release.published_at draft: $release.draft prerelease: $release.prerelease url: $release.html_url assets: $release.assets } } # Get latest version of extension export def get-latest-extension-version [ extension_name: string ] -> string { let extensions = list-gitea-extensions let matching = $extensions | where extension_name == $extension_name if ($matching | length) == 0 { error make { msg: $"Extension not found: ($extension_name)" } } ($matching | first).version } # Publish multiple extensions export def publish-extensions-batch [ extensions_dir: string version: string --extension-type: string = "" ] -> list { let extensions_path = $extensions_dir | path expand # Find all extensions let extensions = if ($extension_type | is-empty) { ls $extensions_path | where type == dir } else { ls $"($extensions_path)/($extension_type)s" | where type == dir } $extensions | each {|ext| print $"Publishing ($ext.name)..." let result = (do { publish-extension-to-gitea $ext.name $version } | complete) if $result.exit_code == 0 { $result.stdout } else { print $"❌ Failed to publish ($ext.name): ($result.stderr)" null } } | where {|x| $x != null} }