provisioning/tools/release/update-registry.nu
2025-10-07 11:12:02 +01:00

616 lines
18 KiB
Plaintext

#!/usr/bin/env nu
# Registry update tool - updates package manager registries
#
# Updates:
# - Homebrew formulas
# - APT/YUM repositories
# - Docker Hub descriptions
# - npm package registry
# - Cargo crates.io
# - GitHub registry packages
use std log
def main [
--registries: string = "homebrew" # Registries to update: homebrew,apt,yum,docker,npm,cargo,all
--version: string = "" # Version to update to (auto-detected if empty)
--package-urls: string = "" # Comma-separated URLs to package files
--registry-config: string = "" # Registry configuration file
--dry-run: bool = false # Show what would be updated without doing it
--auto-commit: bool = false # Automatically commit and push registry updates
--verbose: bool = false # Enable verbose logging
--force: bool = false # Force updates even if version already exists
] -> record {
let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
# Determine version if not provided
let target_version = if $version == "" {
detect_current_version $repo_root
} else {
$version
}
let target_registries = if $registries == "all" {
["homebrew", "apt", "yum", "docker", "npm", "cargo"]
} else {
($registries | split row "," | each { str trim })
}
let registry_config = {
registries: $target_registries
version: $target_version
package_urls: (if $package_urls == "" { [] } else { $package_urls | split row "," | each { str trim } })
registry_config_file: (if $registry_config == "" { "" } else { $registry_config | path expand })
dry_run: $dry_run
auto_commit: $auto_commit
verbose: $verbose
force: $force
repo_root: $repo_root
}
log info $"Starting registry updates with config: ($registry_config)"
# Load registry configuration if provided
let config_data = if $registry_config.registry_config_file != "" {
load_registry_config $registry_config.registry_config_file
} else {
get_default_registry_config
}
# Update each registry
let update_results = $registry_config.registries | each {|registry|
update_registry $registry $registry_config $config_data
}
let summary = {
total_registries: ($registry_config.registries | length)
successful_updates: ($update_results | where status == "success" | length)
failed_updates: ($update_results | where status == "failed" | length)
skipped_updates: ($update_results | where status == "skipped" | length)
version: $registry_config.version
registry_config: $registry_config
results: $update_results
}
if $summary.failed_updates > 0 {
log error $"Registry updates completed with ($summary.failed_updates) failures"
exit 1
} else {
if $registry_config.dry_run {
log info $"Dry run completed - would update ($summary.total_registries) registries"
} else {
log info $"Registry updates completed successfully - ($summary.successful_updates) registries updated"
}
}
return $summary
}
# Detect current version from git tags
def detect_current_version [repo_root: string] -> string {
cd $repo_root
try {
let latest_tag = (git describe --tags --abbrev=0 2>/dev/null | str trim)
if $latest_tag != "" {
# Remove 'v' prefix if present
return ($latest_tag | str replace "^v" "")
}
return "0.1.0"
} catch {
return "0.1.0"
}
}
# Load registry configuration from file
def load_registry_config [config_file: string] -> record {
if not ($config_file | path exists) {
log warning $"Registry config file not found: ($config_file)"
return (get_default_registry_config)
}
try {
open $config_file
} catch {|err|
log warning $"Failed to load registry config: ($err.msg)"
return (get_default_registry_config)
}
}
# Get default registry configuration
def get_default_registry_config [] -> record {
{
homebrew: {
tap_repo: "your-org/homebrew-tap"
formula_name: "provisioning"
description: "Cloud-native infrastructure provisioning and management system"
homepage: "https://github.com/your-org/provisioning"
license: "MIT"
}
apt: {
repository: "https://apt.your-org.com/ubuntu"
distribution: "stable"
component: "main"
keyring: "/usr/share/keyrings/your-org-archive-keyring.gpg"
}
yum: {
repository: "https://yum.your-org.com/centos"
gpg_key: "https://yum.your-org.com/RPM-GPG-KEY-your-org"
}
docker: {
registry: "docker.io"
namespace: "your-org"
image_name: "provisioning"
}
npm: {
registry: "https://registry.npmjs.org"
scope: "@your-org"
package_name: "provisioning"
}
cargo: {
registry: "https://crates.io"
crate_name: "provisioning"
}
}
}
# Update specific registry
def update_registry [
registry: string
registry_config: record
config_data: record
] -> record {
log info $"Updating registry: ($registry)"
let start_time = (date now)
match $registry {
"homebrew" => { update_homebrew $registry_config $config_data }
"apt" => { update_apt_repository $registry_config $config_data }
"yum" => { update_yum_repository $registry_config $config_data }
"docker" => { update_docker_registry $registry_config $config_data }
"npm" => { update_npm_registry $registry_config $config_data }
"cargo" => { update_cargo_registry $registry_config $config_data }
_ => {
log warning $"Unknown registry: ($registry)"
{
registry: $registry
status: "failed"
reason: "unknown registry"
duration: ((date now) - $start_time)
}
}
}
}
# Update Homebrew formula
def update_homebrew [
registry_config: record
config_data: record
] -> record {
log info "Updating Homebrew formula..."
let start_time = (date now)
let homebrew_config = ($config_data.homebrew)
if $registry_config.dry_run {
return {
registry: "homebrew"
status: "success"
formula_name: $homebrew_config.formula_name
version: $registry_config.version
dry_run: true
duration: ((date now) - $start_time)
}
}
try {
# Clone or update tap repository
let tap_dir = $"/tmp/homebrew-tap-(random uuid)"
let tap_url = $"https://github.com/($homebrew_config.tap_repo).git"
if $registry_config.verbose {
log info $"Cloning tap repository: ($tap_url)"
}
git clone $tap_url $tap_dir
cd $tap_dir
# Generate formula
let formula_content = generate_homebrew_formula $registry_config $homebrew_config
# Write formula file
let formula_file = $"Formula/($homebrew_config.formula_name).rb"
mkdir "Formula"
$formula_content | save $formula_file
# Commit and push if auto-commit is enabled
if $registry_config.auto_commit {
git add $formula_file
git commit -m $"Update ($homebrew_config.formula_name) to v($registry_config.version)"
git push origin main
}
# Cleanup
cd /tmp
rm -rf $tap_dir
log info $"Successfully updated Homebrew formula: ($homebrew_config.formula_name)"
{
registry: "homebrew"
status: "success"
formula_name: $homebrew_config.formula_name
version: $registry_config.version
tap_repo: $homebrew_config.tap_repo
auto_committed: $registry_config.auto_commit
duration: ((date now) - $start_time)
}
} catch {|err|
{
registry: "homebrew"
status: "failed"
reason: $err.msg
duration: ((date now) - $start_time)
}
}
}
# Generate Homebrew formula content
def generate_homebrew_formula [
registry_config: record
homebrew_config: record
] -> string {
# Generate download URLs and checksums for different platforms
let platforms = ["linux", "macos"]
let mut platform_blocks = []
for platform in $platforms {
let download_url = $"https://github.com/your-org/provisioning/releases/download/v($registry_config.version)/provisioning-($registry_config.version)-($platform)-complete.tar.gz"
# In a real implementation, you would download and calculate actual SHA256
let sha256 = "placeholder_sha256_hash_would_be_calculated_here"
let platform_block = match $platform {
"linux" => {
$" on_linux do
url \"($download_url)\"
sha256 \"($sha256)\"
end"
}
"macos" => {
$" on_macos do
url \"($download_url)\"
sha256 \"($sha256)\"
end"
}
_ => ""
}
$platform_blocks = ($platform_blocks | append $platform_block)
}
let formula = $"class ($homebrew_config.formula_name | str title-case) < Formula
desc \"($homebrew_config.description)\"
homepage \"($homebrew_config.homepage)\"
license \"($homebrew_config.license)\"
version \"($registry_config.version)\"
($platform_blocks | str join "\n\n")
def install
# Install binaries
bin.install Dir[\"platform/*\"]
# Install libraries
libexec.install Dir[\"core/*\"]
# Install configuration
etc.install Dir[\"config/*\"] => \"provisioning\"
# Create wrapper scripts
(bin/\"provisioning\").write_env_script libexec/\"bin/provisioning\", PROVISIONING_HOME: libexec
end
test do
system \"#{bin}/provisioning\", \"version\"
end
end
"
return $formula
}
# Update APT repository
def update_apt_repository [
registry_config: record
config_data: record
] -> record {
log info "Updating APT repository..."
let start_time = (date now)
if $registry_config.dry_run {
return {
registry: "apt"
status: "success"
version: $registry_config.version
dry_run: true
duration: ((date now) - $start_time)
}
}
# APT repository update would involve:
# 1. Upload .deb packages to repository
# 2. Update Packages index
# 3. Sign repository with GPG key
# 4. Update Release file
log warning "APT repository update not fully implemented"
{
registry: "apt"
status: "skipped"
reason: "not fully implemented"
duration: ((date now) - $start_time)
}
}
# Update YUM repository
def update_yum_repository [
registry_config: record
config_data: record
] -> record {
log info "Updating YUM repository..."
let start_time = (date now)
if $registry_config.dry_run {
return {
registry: "yum"
status: "success"
version: $registry_config.version
dry_run: true
duration: ((date now) - $start_time)
}
}
# YUM repository update would involve:
# 1. Upload .rpm packages to repository
# 2. Run createrepo to update metadata
# 3. Sign packages with GPG key
log warning "YUM repository update not fully implemented"
{
registry: "yum"
status: "skipped"
reason: "not fully implemented"
duration: ((date now) - $start_time)
}
}
# Update Docker registry
def update_docker_registry [
registry_config: record
config_data: record
] -> record {
log info "Updating Docker registry..."
let start_time = (date now)
let docker_config = ($config_data.docker)
if $registry_config.dry_run {
return {
registry: "docker"
status: "success"
image: $"($docker_config.namespace)/($docker_config.image_name):($registry_config.version)"
dry_run: true
duration: ((date now) - $start_time)
}
}
# Docker registry update would involve:
# 1. Tag images with new version
# 2. Push to registry
# 3. Update image descriptions and README
log warning "Docker registry update not fully implemented"
{
registry: "docker"
status: "skipped"
reason: "not fully implemented"
image: $"($docker_config.namespace)/($docker_config.image_name):($registry_config.version)"
duration: ((date now) - $start_time)
}
}
# Update npm registry
def update_npm_registry [
registry_config: record
config_data: record
] -> record {
log info "Updating npm registry..."
let start_time = (date now)
let npm_config = ($config_data.npm)
if $registry_config.dry_run {
return {
registry: "npm"
status: "success"
package: $"($npm_config.scope)/($npm_config.package_name)"
version: $registry_config.version
dry_run: true
duration: ((date now) - $start_time)
}
}
# npm registry update would involve:
# 1. Update package.json with new version
# 2. Build package if needed
# 3. Publish to npm registry
log warning "npm registry update not fully implemented"
{
registry: "npm"
status: "skipped"
reason: "not fully implemented"
package: $"($npm_config.scope)/($npm_config.package_name)"
duration: ((date now) - $start_time)
}
}
# Update Cargo registry
def update_cargo_registry [
registry_config: record
config_data: record
] -> record {
log info "Updating Cargo registry..."
let start_time = (date now)
let cargo_config = ($config_data.cargo)
if $registry_config.dry_run {
return {
registry: "cargo"
status: "success"
crate: $cargo_config.crate_name
version: $registry_config.version
dry_run: true
duration: ((date now) - $start_time)
}
}
# Cargo registry update would involve:
# 1. Update Cargo.toml with new version
# 2. Publish to crates.io
log warning "Cargo registry update not fully implemented"
{
registry: "cargo"
status: "skipped"
reason: "not fully implemented"
crate: $cargo_config.crate_name
duration: ((date now) - $start_time)
}
}
# Show registry status
def "main status" [] {
let homebrew_available = (try { brew --version | complete } catch { { exit_code: 1 } }).exit_code == 0
let docker_available = (try { docker --version | complete } catch { { exit_code: 1 } }).exit_code == 0
let npm_available = (try { npm --version | complete } catch { { exit_code: 1 } }).exit_code == 0
let cargo_available = (try { cargo --version | complete } catch { { exit_code: 1 } }).exit_code == 0
let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
let current_version = (detect_current_version $repo_root)
{
current_version: $current_version
available_tools: {
homebrew: $homebrew_available
docker: $docker_available
npm: $npm_available
cargo: $cargo_available
}
supported_registries: ["homebrew", "apt", "yum", "docker", "npm", "cargo"]
implemented_registries: ["homebrew"] # Only homebrew is fully implemented
}
}
# Generate registry configuration template
def "main init-config" [output_file: string = "registry-config.toml"] {
let config_template = $"# Registry Configuration for Package Distribution
[homebrew]
tap_repo = \"your-org/homebrew-tap\"
formula_name = \"provisioning\"
description = \"Cloud-native infrastructure provisioning and management system\"
homepage = \"https://github.com/your-org/provisioning\"
license = \"MIT\"
[apt]
repository = \"https://apt.your-org.com/ubuntu\"
distribution = \"stable\"
component = \"main\"
keyring = \"/usr/share/keyrings/your-org-archive-keyring.gpg\"
[yum]
repository = \"https://yum.your-org.com/centos\"
gpg_key = \"https://yum.your-org.com/RPM-GPG-KEY-your-org\"
[docker]
registry = \"docker.io\"
namespace = \"your-org\"
image_name = \"provisioning\"
[npm]
registry = \"https://registry.npmjs.org\"
scope = \"@your-org\"
package_name = \"provisioning\"
[cargo]
registry = \"https://crates.io\"
crate_name = \"provisioning\"
"
$config_template | save $output_file
log info $"Generated registry configuration template: ($output_file)"
{
config_file: $output_file
registries_configured: 6
template_generated: true
}
}
# Validate registry configuration
def "main validate" [config_file: string = "registry-config.toml"] {
let config_path = ($config_file | path expand)
if not ($config_path | path exists) {
return {
status: "failed"
reason: $"Configuration file not found: ($config_path)"
}
}
try {
let config = (open $config_path)
let expected_sections = ["homebrew", "apt", "yum", "docker", "npm", "cargo"]
let mut validation_results = []
for section in $expected_sections {
let section_exists = ($section in ($config | columns))
$validation_results = ($validation_results | append {
section: $section
exists: $section_exists
status: (if $section_exists { "valid" } else { "missing" })
})
}
let valid_sections = ($validation_results | where status == "valid" | length)
let total_sections = ($validation_results | length)
{
status: "success"
config_file: $config_path
valid_sections: $valid_sections
total_sections: $total_sections
validation_results: $validation_results
}
} catch {|err|
{
status: "failed"
reason: $"Failed to validate configuration: ($err.msg)"
}
}
}