Skip to main content
Glama

microsandbox

by microsandbox
publish_crates.sh19.3 kB
#!/bin/bash # publish_crates.sh # ---------------- # This script publishes all Cargo packages in the microsandbox workspace to crates.io. # It ensures all packages have the same version and are published in the correct order # based on dependencies. # # Usage: # ./scripts/publish_crates.sh [options] # # Options: # -h, --help Show help message # -v, --version VERSION Version to publish (required, must be semver) # -d, --dry-run Perform a dry run (don't actually publish) # -c, --check-only Only check that versions are consistent # -y, --yes Skip confirmation prompts # -t, --token TOKEN Set crates.io token (if not set via env var) # # Examples: # ./scripts/publish_crates.sh -v 0.1.0 # Publish version 0.1.0 of all crates # ./scripts/publish_crates.sh -v 0.2.0 -d # Test publishing version 0.2.0 (dry run) # ./scripts/publish_crates.sh -v 0.1.0 -y -t TOKEN # Publish with token, no confirmation # # Note: This script assumes you're already logged in to crates.io using cargo login, # or that you provide a token with the -t option. # Exit on any error set -e # Color variables RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Get the project root directory PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # Packages in the workspace (in dependency order) PACKAGES=( "microsandbox-utils" "microsandbox-core" "microsandbox-server" "microsandbox-portal" "microsandbox-cli" ) # Default values VERSION="" DRY_RUN=false CHECK_ONLY=false SKIP_CONFIRM=false CRATES_TOKEN="" # Store the last package name for comparison later LAST_PACKAGE="${PACKAGES[4]}" # microsandbox-cli is the last package # Display usage information function show_usage { echo -e "${BLUE}Usage:${NC} $0 [OPTIONS]" echo echo "Options:" echo " -h, --help Show this help message" echo " -v, --version VERSION Version to publish (required, must be semver)" echo " -d, --dry-run Perform a dry run (don't actually publish)" echo " -c, --check-only Only check that versions are consistent" echo " -y, --yes Skip confirmation prompts" echo " -t, --token TOKEN Set crates.io token (if not set via env var)" echo echo "Examples:" echo " $0 -v 0.1.0 # Publish version 0.1.0 of all crates" echo " $0 -v 0.2.0 -d # Test publishing version 0.2.0 (dry run)" echo " $0 -v 0.1.0 -y -t TOKEN # Publish with token, no confirmation" echo } # Logging functions info() { printf "${GREEN}:: %s${NC}\n" "$1" } warn() { printf "${YELLOW}:: %s${NC}\n" "$1" } error() { printf "${RED}:: %s${NC}\n" "$1" } # Check if version is valid semver validate_version() { local version=$1 if ! [[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9\.]+)?(\+[a-zA-Z0-9\.]+)?$ ]]; then error "Invalid version format: $version" echo "Version must be in semver format (e.g., 1.2.3, 1.2.3-alpha, 1.2.3+metadata)" exit 1 fi } # Check if cargo is available check_cargo() { if ! command -v cargo &> /dev/null; then error "Cargo is not installed or not in PATH" exit 1 fi } # Check if user is logged in to crates.io check_crates_auth() { # If token is provided, set it up if [ -n "$CRATES_TOKEN" ]; then info "Setting provided crates.io token" if [ "$DRY_RUN" = false ]; then cargo login "$CRATES_TOKEN" >/dev/null else info "[DRY RUN] Would set crates.io token" fi return 0 fi # Otherwise check if already logged in if [ ! -f "$HOME/.cargo/credentials.toml" ] && [ ! -f "$HOME/.cargo/credentials" ]; then error "Not logged in to crates.io. Use 'cargo login' or provide token with -t/--token" exit 1 fi } # Update version in Cargo.toml files update_versions() { local version=$1 local workspace_toml="$PROJECT_ROOT/Cargo.toml" info "Updating version in workspace Cargo.toml to $version" if [ "$DRY_RUN" = false ]; then # Update version in workspace.package section of Cargo.toml sed -i.bak "/^\[workspace\.package\]/,/^\[/ s/^version = \"[0-9]*\.[0-9]*\.[0-9]*.*\"/version = \"$version\"/" "$workspace_toml" rm "${workspace_toml}.bak" else info "[DRY RUN] Would update version in $workspace_toml [workspace.package] section to $version" fi # For crates that don't use workspace versioning, update their version for package in "${PACKAGES[@]}"; do local cargo_toml="$PROJECT_ROOT/$package/Cargo.toml" if [ -f "$cargo_toml" ]; then # Check if the package uses workspace versioning if grep -q "version.workspace = true" "$cargo_toml"; then info "$package uses workspace versioning, no direct version update needed" else info "Updating version in $package/Cargo.toml to $version" if [ "$DRY_RUN" = false ]; then # Update within [package] section only sed -i.bak "/^\[package\]/,/^\[/ s/^version = \"[0-9]*\.[0-9]*\.[0-9]*.*\"/version = \"$version\"/" "$cargo_toml" rm "${cargo_toml}.bak" else info "[DRY RUN] Would update direct version in $cargo_toml" fi fi # Update dependencies on other workspace packages that don't use path-only dependencies if [ "$DRY_RUN" = false ]; then for dep in "${PACKAGES[@]}"; do # Only update if the dependency specifies a version and not just a path if grep -q "$dep = { \?version" "$cargo_toml"; then sed -i.bak "s/$dep = { \?version = \"[0-9]*\.[0-9]*\.[0-9]*.*\"/$dep = { version = \"$version\"/" "$cargo_toml" rm "${cargo_toml}.bak" fi done else info "[DRY RUN] Would update dependencies in $cargo_toml" fi else warn "Could not find Cargo.toml for package $package" fi done info "All version numbers updated to $version" } # Check if versions are consistent check_versions() { # Get the workspace version from the [workspace.package] section local workspace_section=$(sed -n '/^\[workspace\.package\]/,/^\[/p' "$PROJECT_ROOT/Cargo.toml") local workspace_version=$(echo "$workspace_section" | grep 'version =' | head -1 | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+.*)".*/\1/') if [ -z "$workspace_version" ]; then error "Could not find workspace version in Cargo.toml" exit 1 fi info "Workspace version: $workspace_version" local consistent=true for package in "${PACKAGES[@]}"; do local cargo_toml="$PROJECT_ROOT/$package/Cargo.toml" if [ -f "$cargo_toml" ]; then # Check if package uses workspace versioning if grep -q "version.workspace = true" "$cargo_toml"; then info "$package version: $workspace_version (via workspace)" else # Extract the package version from the [package] section local package_section=$(sed -n '/^\[package\]/,/^\[/p' "$cargo_toml") local package_version=$(echo "$package_section" | grep 'version =' | head -1 | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+.*)".*/\1/') if [ -z "$package_version" ]; then error "Could not find version in $package" consistent=false elif [ "$package_version" != "$workspace_version" ]; then error "Version mismatch in $package: $package_version (expected $workspace_version)" consistent=false else info "$package version: $package_version" fi fi else warn "Could not find Cargo.toml for package $package" fi done if [ "$consistent" = false ]; then error "Versions are not consistent across the workspace" exit 1 else info "All versions are consistent with workspace version: $workspace_version" fi } # Publish a single crate publish_crate() { local package=$1 local package_dir="$PROJECT_ROOT/$package" if [ ! -d "$package_dir" ]; then error "Package directory not found: $package_dir" return 1 fi info "Publishing $package v$VERSION to crates.io" cd "$package_dir" if [ "$DRY_RUN" = true ]; then # For dry runs, skip the verification step entirely to avoid dependency issues info "[DRY RUN] Simulating cargo publish for $package v$VERSION" # Use cargo publish with --dry-run and --no-verify to skip checking dependencies on crates.io if cargo publish --dry-run --no-verify --allow-dirty; then info "[DRY RUN] Package verification successful for $package v$VERSION" else error "[DRY RUN] Failed to verify package $package" exit 1 fi else # Real publish - should proceed only if all dependencies are properly available # Capture both stdout and stderr output=$(cargo publish 2>&1) exit_code=$? # Check if the output contains "already exists" error message if [ $exit_code -ne 0 ]; then if echo "$output" | grep -q "already exists"; then warn "Package $package v$VERSION already exists on crates.io, skipping..." # Return success so the script continues return 0 else # It's a different error, so print it and exit error "Failed to publish $package v$VERSION" echo "$output" exit 1 fi else info "Successfully published $package v$VERSION" fi fi # Return to project root cd "$PROJECT_ROOT" } # Check if a package is available on crates.io wait_for_package_availability() { local package=$1 local version=$2 local max_attempts=40 # roughly 2 minutes (40 * 3s) local attempt=1 # In dry-run mode we skip the remote check entirely – we assume success. if [ "$DRY_RUN" = true ]; then info "[DRY RUN] Skipping crates.io availability check for $package" return 0 fi # If a package was already found to exist during publication, we don't need to wait if [ -n "$SKIP_WAIT_FOR_$package" ]; then info "Package $package v$version was already on crates.io, skipping wait check" return 0 fi info "Waiting for $package v$version to be available on crates.io…" while [ $attempt -le $max_attempts ]; do # Query crates.io via its API response=$(curl -s "https://crates.io/api/v1/crates/${package}/${version}") # Check if the response contains successful version data # Look for both the version number and the package name in the response if echo "$response" | grep -q "\"num\":\"${version}\"" && \ echo "$response" | grep -q "\"crate\":\"${package}\""; then info "$package v$version is now available on crates.io!" return 0 fi # Debug output if needed if [ $attempt -eq 1 ] || [ $((attempt % 10)) -eq 0 ]; then # Log the API response occasionally for debugging echo "API response (attempt $attempt):" echo "$response" | head -10 fi sleep 3 attempt=$((attempt + 1)) done warn "Timed out waiting for $package v$version to become available." # Even if we time out, we'll check one last time by making a direct HTTP request to the package page # This is a fallback in case the API is inconsistent if curl -s -I "https://crates.io/crates/${package}/${version}" | grep -q "HTTP/"; then info "Package $package v$version appears to be available on crates.io website, continuing..." return 0 fi # Ask the user what to do read -p "Continue with publishing anyway? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then info "Continuing with publishing as requested..." return 0 else error "Publishing aborted due to dependency availability issues." exit 1 fi } # Publish all crates in order publish_crates() { info "Publishing all crates with version $VERSION" # Temporarily disable strict error handling for this function set +e if [ "$SKIP_CONFIRM" = false ]; then read -p "Are you sure you want to publish all crates with version $VERSION? (y/n) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then info "Publishing cancelled." # Re-enable strict error handling before exiting set -e exit 0 fi fi # Track published packages to know which ones we need to wait for local published_packages=() # Track already existing packages to skip wait checks declare -A already_exists_packages for package in "${PACKAGES[@]}"; do # First check if we need to wait for any dependencies local has_unpublished_deps=false for published in "${published_packages[@]}"; do # Check if the current package depends on previously published packages # Handle both formats: # 1. microsandbox-utils = { version = "0.1.0", ... } # 2. microsandbox-utils.workspace = true if grep -q "$published = { \?version" "$PROJECT_ROOT/$package/Cargo.toml" || \ grep -q "$published\.workspace = true" "$PROJECT_ROOT/$package/Cargo.toml"; then if [ "$DRY_RUN" = false ]; then info "$package depends on $published, waiting for it to be available..." # Skip waiting if we already know it exists if [ "${already_exists_packages[$published]}" = "1" ]; then info "Dependency $published v$VERSION already exists on crates.io, no need to wait" else wait_for_package_availability "$published" "$VERSION" fi else info "$package depends on $published (dependency noted for dry run)" fi fi done # Now publish the package info "Publishing $package v$VERSION to crates.io" cd "$PROJECT_ROOT/$package" if [ "$DRY_RUN" = true ]; then # For dry runs, skip the verification step entirely to avoid dependency issues info "[DRY RUN] Simulating cargo publish for $package v$VERSION" # Use cargo publish with --dry-run and --no-verify to skip checking dependencies on crates.io cargo publish --dry-run --no-verify --allow-dirty if [ $? -eq 0 ]; then info "[DRY RUN] Package verification successful for $package v$VERSION" else error "[DRY RUN] Failed to verify package $package" # Re-enable strict error handling before exiting set -e exit 1 fi else # Real publish - should proceed only if all dependencies are properly available # Capture both stdout and stderr output=$(cargo publish 2>&1) exit_code=$? # Check if the output contains "already exists" error message if [ $exit_code -ne 0 ]; then if echo "$output" | grep -q "already exists"; then warn "Package $package v$VERSION already exists on crates.io, skipping..." # Mark this package as already existing to skip waiting for it later already_exists_packages[$package]=1 else # It's a different error, so print it and exit error "Failed to publish $package v$VERSION" echo "$output" # Re-enable strict error handling before exiting set -e exit 1 fi else info "Successfully published $package v$VERSION" fi fi # Return to project root cd "$PROJECT_ROOT" # Add to the list of published packages published_packages+=("$package") # After publishing (real run), wait until crates.io shows the crate before proceeding if [ "$DRY_RUN" = false ] && [ "$package" != "$LAST_PACKAGE" ]; then # Skip waiting if we already know it exists if [ "${already_exists_packages[$package]}" = "1" ]; then info "Package $package v$VERSION already exists on crates.io, no need to wait" else wait_for_package_availability "$package" "$VERSION" fi fi done info "All crates published successfully with version $VERSION" # Re-enable strict error handling set -e } # Parse command line arguments if [ $# -eq 0 ]; then show_usage exit 1 fi while [[ $# -gt 0 ]]; do key="$1" case $key in -h|--help) show_usage exit 0 ;; -v|--version) if [ -z "$2" ] || [[ "$2" == -* ]]; then error "No version provided after -v/--version option" show_usage exit 1 fi VERSION="$2" shift ;; -d|--dry-run) DRY_RUN=true info "Running in dry-run mode, no packages will be published" ;; -c|--check-only) CHECK_ONLY=true info "Only checking version consistency, no packages will be published" ;; -y|--yes) SKIP_CONFIRM=true info "Skipping confirmation prompts" ;; -t|--token) if [ -z "$2" ] || [[ "$2" == -* ]]; then error "No token provided after -t/--token option" show_usage exit 1 fi CRATES_TOKEN="$2" shift ;; *) error "Unknown option: ${1}" show_usage exit 1 ;; esac shift done # Validate required parameters if [ -z "$VERSION" ] && [ "$CHECK_ONLY" = false ]; then error "Version is required (-v/--version)" show_usage exit 1 fi # Validate version if provided if [ -n "$VERSION" ]; then validate_version "$VERSION" fi # Check prerequisites check_cargo # Perform the requested operations if [ "$CHECK_ONLY" = true ]; then check_versions exit 0 fi # Update versions if [ -n "$VERSION" ]; then update_versions "$VERSION" fi # Check versions after updating check_versions # Authentication check is only needed for actual publishing if [ "$DRY_RUN" = false ]; then check_crates_auth fi # Publish the crates publish_crates

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/microsandbox/microsandbox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server