384 lines
11 KiB
Text
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}
|
|
}
|