# Ceylon Runtime - Smart Publish Script
# Publishes crates in correct dependency order, skipping already-published versions
# Usage: .\publish-smart.ps1 [-DryRun] [-AllowDirty]
param(
[switch]$DryRun, # Preview what would be published without actually publishing
[switch]$AllowDirty # Allow uncommitted changes
)
$ErrorActionPreference = "Stop"
$runtimeDir = $PSScriptRoot
$cratesDir = Join-Path $runtimeDir "crates"
# IMPORTANT: Dependency order matters!
# 1. Core crates (no internal deps)
# 2. ceylon-runtime (depends on core crates)
# 3. Crates that depend on ceylon-runtime (server, cli)
$coreCrates = @(
@{ Name = "ceylon-core"; Path = "ceylon-core" },
@{ Name = "ceylon-observability"; Path = "ceylon-observability" },
@{ Name = "ceylon-llm"; Path = "ceylon-llm" },
@{ Name = "ceylon-local"; Path = "ceylon-local" },
@{ Name = "ceylon-memory"; Path = "ceylon-memory" },
@{ Name = "ceylon-mcp"; Path = "ceylon-mcp" }
)
# These depend on ceylon-runtime, so must be published AFTER it
$dependentCrates = @(
@{ Name = "ceylon-server"; Path = "ceylon-server" },
@{ Name = "ceylon-cli"; Path = "ceylon-cli" }
)
function Get-LocalVersion {
param([string]$CargoTomlPath)
$content = Get-Content $CargoTomlPath -Raw
if ($content -match 'version\s*=\s*"([^"]+)"') {
return $matches[1]
}
return $null
}
function Get-CratesIoVersion {
param([string]$CrateName)
try {
$response = Invoke-RestMethod -Uri "https://crates.io/api/v1/crates/$CrateName" -TimeoutSec 10
return $response.crate.max_version
}
catch {
return $null
}
}
function Compare-Versions {
param([string]$Local, [string]$Published)
if (-not $Published) { return "NEW" }
if ($Local -eq $Published) { return "SAME" }
$localParts = $Local -split '\.'
$pubParts = $Published -split '\.'
for ($i = 0; $i -lt 3; $i++) {
$l = [int]($localParts[$i] -replace '[^0-9]', '')
$p = [int]($pubParts[$i] -replace '[^0-9]', '')
if ($l -gt $p) { return "NEWER" }
if ($l -lt $p) { return "OLDER" }
}
return "SAME"
}
function Check-Crate {
param($Crate, [string]$BasePath)
$cratePath = Join-Path $BasePath $Crate.Path
$cargoToml = Join-Path $cratePath "Cargo.toml"
if (-not (Test-Path $cargoToml)) { return $null }
$localVersion = Get-LocalVersion $cargoToml
$publishedVersion = Get-CratesIoVersion $Crate.Name
$comparison = Compare-Versions $localVersion $publishedVersion
return @{
Name = $Crate.Name
Path = $cratePath
LocalVersion = $localVersion
PublishedVersion = $publishedVersion
Status = $comparison
}
}
function Show-CrateStatus {
param($Info)
switch ($Info.Status) {
"NEW" { Write-Host " [NEW] $($Info.Name) v$($Info.LocalVersion)" -ForegroundColor Green }
"NEWER" { Write-Host " [BUMP] $($Info.Name) v$($Info.LocalVersion) (was: v$($Info.PublishedVersion))" -ForegroundColor Green }
"SAME" { Write-Host " [SKIP] $($Info.Name) v$($Info.LocalVersion) (already published)" -ForegroundColor DarkGray }
"OLDER" { Write-Host " [WARN] $($Info.Name) v$($Info.LocalVersion) < v$($Info.PublishedVersion)" -ForegroundColor Yellow }
}
}
function Publish-Crate {
param($Info, [switch]$AllowDirty)
Write-Host "Publishing $($Info.Name) v$($Info.LocalVersion)..." -ForegroundColor Yellow
Push-Location $Info.Path
try {
$args = @("publish")
if ($AllowDirty) { $args += "--allow-dirty" }
& cargo @args
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to publish $($Info.Name)" -ForegroundColor Red
return $false
}
Write-Host "Successfully published $($Info.Name)" -ForegroundColor Green
return $true
}
finally {
Pop-Location
}
}
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Ceylon Runtime - Smart Publish" -ForegroundColor Cyan
if ($DryRun) { Write-Host " (DRY RUN)" -ForegroundColor Yellow }
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Phase 1: Check core crates
Write-Host "Phase 1: Core Crates" -ForegroundColor Magenta
$coreToPublish = @()
foreach ($crate in $coreCrates) {
$info = Check-Crate $crate $cratesDir
if ($info) {
Show-CrateStatus $info
if ($info.Status -in @("NEW", "NEWER")) { $coreToPublish += $info }
}
}
# Phase 2: Check ceylon-runtime (depends on core crates)
Write-Host ""
Write-Host "Phase 2: Main Runtime" -ForegroundColor Magenta
$runtimeInfo = @{
Name = "ceylon-runtime"
Path = $runtimeDir
LocalVersion = Get-LocalVersion (Join-Path $runtimeDir "Cargo.toml")
PublishedVersion = Get-CratesIoVersion "ceylon-runtime"
}
$runtimeInfo.Status = Compare-Versions $runtimeInfo.LocalVersion $runtimeInfo.PublishedVersion
Show-CrateStatus $runtimeInfo
$publishRuntime = $runtimeInfo.Status -in @("NEW", "NEWER")
# Phase 3: Check dependent crates (depend on ceylon-runtime)
Write-Host ""
Write-Host "Phase 3: Dependent Crates" -ForegroundColor Magenta
$dependentToPublish = @()
foreach ($crate in $dependentCrates) {
$info = Check-Crate $crate $cratesDir
if ($info) {
Show-CrateStatus $info
if ($info.Status -in @("NEW", "NEWER")) { $dependentToPublish += $info }
}
}
# Summary
Write-Host ""
Write-Host "----------------------------------------" -ForegroundColor DarkGray
$totalCount = $coreToPublish.Count + $(if ($publishRuntime) { 1 } else { 0 }) + $dependentToPublish.Count
if ($totalCount -eq 0) {
Write-Host ""
Write-Host "Nothing to publish - all versions up to date!" -ForegroundColor Green
exit 0
}
Write-Host ""
Write-Host "Publish order:" -ForegroundColor Cyan
$order = 1
foreach ($c in $coreToPublish) { Write-Host " $order. $($c.Name)" -ForegroundColor White; $order++ }
if ($publishRuntime) { Write-Host " $order. ceylon-runtime" -ForegroundColor White; $order++ }
foreach ($c in $dependentToPublish) { Write-Host " $order. $($c.Name)" -ForegroundColor White; $order++ }
Write-Host ""
if ($DryRun) {
Write-Host "DRY RUN complete. Run without -DryRun to publish." -ForegroundColor Yellow
exit 0
}
# Publish in correct order
$waitTime = 30
# Phase 1: Publish core crates
foreach ($info in $coreToPublish) {
if (-not (Publish-Crate $info -AllowDirty:$AllowDirty)) { exit 1 }
Write-Host "Waiting ${waitTime}s for crates.io..." -ForegroundColor Gray
Start-Sleep -Seconds $waitTime
}
# Phase 2: Publish ceylon-runtime
if ($publishRuntime) {
if (-not (Publish-Crate $runtimeInfo -AllowDirty:$AllowDirty)) { exit 1 }
Write-Host "Waiting ${waitTime}s for crates.io..." -ForegroundColor Gray
Start-Sleep -Seconds $waitTime
}
# Phase 3: Publish dependent crates (AFTER ceylon-runtime)
foreach ($info in $dependentToPublish) {
if (-not (Publish-Crate $info -AllowDirty:$AllowDirty)) { exit 1 }
Write-Host "Waiting ${waitTime}s for crates.io..." -ForegroundColor Gray
Start-Sleep -Seconds $waitTime
}
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " All crates published successfully!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan