provisioning-core/nulib/domain/gitea/extension_publish.nu

384 lines
11 KiB
Text

# 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}
}