#!/usr/bin/env bash set -euo pipefail show_help() { cat <<'EOF' sf-retrieve — wrapper for streamlined metadata retrieval USAGE: sf-retrieve -o (-t | -m | -p ) [-n ] [-d ] [-h] OPTIONS: -o Org alias or username to retrieve from (required) -t Comma-separated metadata types (ApexClass,CustomObject,Flow,etc.) -n Comma-separated component names (optional, works with -t) -m Path to manifest file (package.xml) -p Package name to retrieve -d Target directory for retrieved metadata (default: force-app) -h Show this help EXAMPLES: 1) Retrieve all Apex classes: sf-retrieve -o PROD-ORG -t "ApexClass" 2) Retrieve specific classes: sf-retrieve -o PROD-ORG -t "ApexClass" -n "MyClass,AnotherClass" 3) Retrieve multiple metadata types: sf-retrieve -o PROD-ORG -t "ApexClass,CustomObject,Flow" 4) Retrieve using manifest: sf-retrieve -o PROD-ORG -m "manifest/package.xml" 5) Retrieve to specific directory: sf-retrieve -o PROD-ORG -t "ApexClass" -d "retrieved-metadata" 6) Retrieve installed package: sf-retrieve -o PROD-ORG -p "MyPackage" COMMON METADATA TYPES: - ApexClass, ApexTrigger, ApexComponent, ApexPage - CustomObject, CustomField, CustomTab - Flow, ProcessBuilder, WorkflowRule - Layout, Profile, PermissionSet - StaticResource, ContentAsset - CustomApplication, CustomLabel Notes: - Use -t with -n to retrieve specific components of a type - Use -m for complex retrievals with manifest files - Use -p 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 while getopts ":o:t:n:m:p:d:h" opt; do case "$opt" in o) ORG="$OPTARG" ;; t) TYPES="$OPTARG" ;; n) NAMES="$OPTARG" ;; m) MANIFEST="$OPTARG" ;; p) PACKAGE="$OPTARG" ;; d) TARGET_DIR="$OPTARG" ;; h) show_help; exit 0 ;; \?) echo "Unknown option: -$OPTARG" >&2; echo; show_help; exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2; echo; show_help; exit 1 ;; esac done # Validate required parameters if [[ -z "$ORG" ]]; then echo "Error: Org (-o) 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 -t (types), -m (manifest), or -p (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 -t, -m, and -p 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