#!/usr/bin/env bash set -euo pipefail show_help() { cat <<'EOF' sf-retrieve — wrapper for streamlined metadata retrieval USAGE: sf-retrieve -to (-tp | -mn | -pk ) [-nm ] [-dr ] [-hp] OPTIONS: -to, --target-org Org alias or username to retrieve from (required) -tp, --types Comma-separated metadata types (ApexClass,CustomObject,Flow,etc.) -nm, --names Comma-separated component names (optional, works with -tp) -mn, --manifest Path to manifest file (package.xml) -pk, --package Package name to retrieve -dr, --directory Target directory for retrieved metadata (default: force-app) -hp, --help Show this help EXAMPLES: 1) Retrieve all Apex classes: sf-retrieve -to PROD-ORG -tp "ApexClass" 2) Retrieve specific classes: sf-retrieve -to PROD-ORG -tp "ApexClass" -nm "MyClass,AnotherClass" 3) Retrieve multiple metadata types: sf-retrieve -to PROD-ORG -tp "ApexClass,CustomObject,Flow" 4) Retrieve using manifest: sf-retrieve -to PROD-ORG -mn "manifest/package.xml" 5) Retrieve to specific directory: sf-retrieve -to PROD-ORG -tp "ApexClass" -dr "retrieved-metadata" 6) Retrieve installed package: sf-retrieve -to PROD-ORG -pk "MyPackage" COMMON METADATA TYPES: - ApexClass, ApexTrigger, ApexComponent, ApexPage - CustomObject, CustomField, CustomTab - Flow, ProcessBuilder, WorkflowRule - Layout, Profile, PermissionSet - StaticResource, ContentAsset - CustomApplication, CustomLabel Notes: - Use -tp with -nm to retrieve specific components of a type - Use -mn for complex retrievals with manifest files - Use -pk to retrieve entire packages - Retrieved metadata will be in source format EOF } # Default values ORG="" TYPES="" NAMES="" MANIFEST="" PACKAGE="" TARGET_DIR="force-app" if [[ $# -eq 0 ]]; then show_help exit 0 fi # Parse arguments manually for two-character options while [[ $# -gt 0 ]]; do case $1 in -to|--target-org) if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then ORG="$2" shift 2 else echo "Error: -to requires a target org argument" >&2 show_help exit 1 fi ;; -tp|--types) if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then TYPES="$2" shift 2 else echo "Error: -tp requires a metadata types argument" >&2 show_help exit 1 fi ;; -nm|--names) if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then NAMES="$2" shift 2 else echo "Error: -nm requires a names argument" >&2 show_help exit 1 fi ;; -mn|--manifest) if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then MANIFEST="$2" shift 2 else echo "Error: -mn requires a manifest file argument" >&2 show_help exit 1 fi ;; -pk|--package) if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then PACKAGE="$2" shift 2 else echo "Error: -pk requires a package name argument" >&2 show_help exit 1 fi ;; -dr|--directory) if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then TARGET_DIR="$2" shift 2 else echo "Error: -dr requires a directory argument" >&2 show_help exit 1 fi ;; -hp|--help) show_help exit 0 ;; -*) echo "Unknown option: $1" >&2 echo show_help exit 1 ;; *) echo "Unexpected argument: $1" >&2 echo show_help exit 1 ;; esac done # Validate required parameters if [[ -z "$ORG" ]]; then echo "Error: Org (-to) is required." >&2 echo show_help exit 1 fi # Validate that at least one retrieval method is specified if [[ -z "$TYPES" && -z "$MANIFEST" && -z "$PACKAGE" ]]; then echo "Error: Must specify -tp (types), -mn (manifest), or -pk (package)." >&2 echo show_help exit 1 fi # Validate that only one retrieval method is used method_count=0 [[ -n "$TYPES" ]] && ((method_count++)) [[ -n "$MANIFEST" ]] && ((method_count++)) [[ -n "$PACKAGE" ]] && ((method_count++)) if [[ $method_count -gt 1 ]]; then echo "Error: Cannot use -tp, -mn, and -pk together. Choose one method." >&2 echo show_help exit 1 fi # Validate manifest file exists if [[ -n "$MANIFEST" && ! -f "$MANIFEST" ]]; then echo "Error: Manifest file '$MANIFEST' not found." >&2 exit 1 fi # Silent environment check if ! command -v sf >/dev/null 2>&1; then echo "❌ Salesforce CLI (sf) not found!" echo echo "Running environment check to help you get started..." echo SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [[ -x "$SCRIPT_DIR/sf-check" ]]; then "$SCRIPT_DIR/sf-check" elif command -v sf-check >/dev/null 2>&1; then sf-check else echo "sf-check not found. Please install the Salesforce CLI from:" echo "https://developer.salesforce.com/tools/sfdxcli" fi exit 1 fi # Build the command CMD=(sf project retrieve start) CMD+=(--target-org "$ORG") # Add retrieval method if [[ -n "$TYPES" ]]; then # Split types by comma and add each as separate --metadata parameter IFS=',' read -ra TYPES_ARR <<< "$TYPES" for TYPE in "${TYPES_ARR[@]}"; do TYPE=$(echo "$TYPE" | xargs) # Trim whitespace if [[ -n "$TYPE" ]]; then if [[ -n "$NAMES" ]]; then # If names are specified, add each name for this type IFS=',' read -ra NAMES_ARR <<< "$NAMES" for NAME in "${NAMES_ARR[@]}"; do NAME=$(echo "$NAME" | xargs) # Trim whitespace if [[ -n "$NAME" ]]; then CMD+=(--metadata "$TYPE:$NAME") fi done else # No names specified, retrieve all of this type CMD+=(--metadata "$TYPE") fi fi done elif [[ -n "$MANIFEST" ]]; then CMD+=(--manifest "$MANIFEST") elif [[ -n "$PACKAGE" ]]; then CMD+=(--package-name "$PACKAGE") fi # Add target directory CMD+=(--output-dir "$TARGET_DIR") # Show what we're retrieving echo "🔄 Retrieving metadata from org '$ORG'..." if [[ -n "$TYPES" ]]; then echo " Types: $TYPES" [[ -n "$NAMES" ]] && echo " Names: $NAMES" elif [[ -n "$MANIFEST" ]]; then echo " Manifest: $MANIFEST" elif [[ -n "$PACKAGE" ]]; then echo " Package: $PACKAGE" fi echo " Target: $TARGET_DIR" echo echo ">>> Running: ${CMD[*]}" echo if "${CMD[@]}"; then echo echo "✅ Metadata retrieved successfully!" echo "📁 Check the '$TARGET_DIR' directory for retrieved components." # Show summary of what was retrieved if command -v find >/dev/null 2>&1 && [[ -d "$TARGET_DIR" ]]; then echo echo "📊 Retrieved components summary:" find "$TARGET_DIR" -type f -name "*.xml" -o -name "*.cls" -o -name "*.trigger" -o -name "*.js" -o -name "*.html" | \ sed 's|.*/||' | sort | uniq -c | sort -nr | head -10 fi else echo echo "❌ Failed to retrieve metadata" exit 1 fi