Add file conflict handling to minio-upload skill
Implement comprehensive file conflict detection and resolution: - Check for existing files before upload using mc stat (fixes mc ls bug) - Interactive prompt when conflict detected: overwrite, skip, backup, rename - Command-line options for non-interactive use: --force, --skip, --backup, --rename - Auto-rename with numeric suffix when file exists - Backup existing files with .bak extension before overwriting - Update SKILL.md with conflict handling documentation and examples Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,47 @@ Upload to a specific vault:
|
||||
.claude/skills/minio-upload/scripts/upload_to_obsidian.sh path/to/file.md "journal"
|
||||
```
|
||||
|
||||
## File Conflict Handling
|
||||
|
||||
**CRITICAL**: When a file with the same name already exists in the target vault, the script will prompt you to choose an action (unless an option is specified).
|
||||
|
||||
### Interactive Prompt (Default)
|
||||
|
||||
If the file exists and no option is provided, you'll see:
|
||||
```
|
||||
⚠️ File 'filename.md' already exists in vault 'main'
|
||||
Choose an action:
|
||||
[o] Overwrite - Replace existing file
|
||||
[s] Skip - Cancel upload
|
||||
[b] Backup - Backup existing file (.bak) then overwrite
|
||||
[r] Rename - Upload with new name (adds numeric suffix)
|
||||
```
|
||||
|
||||
### Command-Line Options
|
||||
|
||||
You can specify the behavior explicitly to skip the prompt:
|
||||
|
||||
- `--force` - Overwrite existing files without prompting
|
||||
- `--skip` - Skip upload if file already exists
|
||||
- `--backup` - Backup existing file (creates `.bak` file) before overwriting
|
||||
- `--rename` - Automatically rename with numeric suffix (e.g., `file-1.md`, `file-2.md`)
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Force overwrite without prompting
|
||||
upload_to_obsidian.sh file.md main --force
|
||||
|
||||
# Skip if file exists
|
||||
upload_to_obsidian.sh file.md journal --skip
|
||||
|
||||
# Backup before overwriting
|
||||
upload_to_obsidian.sh file.md work --backup
|
||||
|
||||
# Auto-rename if conflict
|
||||
upload_to_obsidian.sh file.md personal --rename
|
||||
```
|
||||
|
||||
## Vault Configuration
|
||||
|
||||
**CRITICAL RULE**: When no vault is explicitly specified by the user, files MUST be uploaded to the `main` vault only. Do NOT assume or invent other vault names.
|
||||
@@ -52,8 +93,10 @@ The MinIO obsidian bucket contains Obsidian vaults as top-level directories:
|
||||
|
||||
1. **Verify file exists** - Check that the source file is accessible
|
||||
2. **Determine target vault** - Use specified vault name or default to `main`
|
||||
3. **Upload with mc** - Execute `mc cp` command to transfer file
|
||||
4. **Confirm success** - Verify upload completed and report target location
|
||||
3. **Check for conflicts** - Detect if file already exists in target vault
|
||||
4. **Resolve conflict** - Prompt user or use specified option to handle conflict
|
||||
5. **Upload with mc** - Execute `mc cp` command to transfer file
|
||||
6. **Confirm success** - Verify upload completed and report target location
|
||||
|
||||
## Error Handling
|
||||
|
||||
|
||||
@@ -1,27 +1,77 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Upload files to MinIO obsidian bucket
|
||||
# Usage: upload_to_obsidian.sh <file_path> [subpath]
|
||||
# Usage: upload_to_obsidian.sh <file_path> [subpath] [options]
|
||||
#
|
||||
# Arguments:
|
||||
# file_path - Path to the file to upload
|
||||
# subpath - Optional subpath within obsidian bucket (default: main)
|
||||
# subpath - Optional subpath/vault within obsidian bucket (default: main)
|
||||
#
|
||||
# Options:
|
||||
# --force - Overwrite existing files without prompting
|
||||
# --skip - Skip upload if file already exists
|
||||
# --backup - Backup existing file before overwriting (.bak extension)
|
||||
# --rename - Automatically rename if file exists (adds numeric suffix)
|
||||
#
|
||||
# Examples:
|
||||
# upload_to_obsidian.sh temp/file.md
|
||||
# upload_to_obsidian.sh temp/file.md notes/2024
|
||||
# upload_to_obsidian.sh temp/file.md "archive/old projects"
|
||||
# upload_to_obsidian.sh temp/file.md journal
|
||||
# upload_to_obsidian.sh temp/file.md main --force
|
||||
# upload_to_obsidian.sh temp/file.md work --skip
|
||||
|
||||
set -e
|
||||
|
||||
# Check arguments
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <file_path> [subpath]" >&2
|
||||
# Initialize variables
|
||||
FORCE=false
|
||||
SKIP=false
|
||||
BACKUP=false
|
||||
RENAME=false
|
||||
FILE_PATH=""
|
||||
SUBPATH="main"
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
--skip)
|
||||
SKIP=true
|
||||
shift
|
||||
;;
|
||||
--backup)
|
||||
BACKUP=true
|
||||
shift
|
||||
;;
|
||||
--rename)
|
||||
RENAME=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Error: Unknown option '$1'" >&2
|
||||
echo "Usage: $0 <file_path> [subpath] [--force|--skip|--backup|--rename]" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ -z "$FILE_PATH" ]; then
|
||||
FILE_PATH="$1"
|
||||
elif [ "$SUBPATH" = "main" ]; then
|
||||
SUBPATH="$1"
|
||||
else
|
||||
echo "Error: Too many arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
FILE_PATH="$1"
|
||||
SUBPATH="${2:-main}" # Default to 'main' if not specified
|
||||
# Check file path argument
|
||||
if [ -z "$FILE_PATH" ]; then
|
||||
echo "Usage: $0 <file_path> [subpath] [--force|--skip|--backup|--rename]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate file exists
|
||||
if [ ! -f "$FILE_PATH" ]; then
|
||||
@@ -38,6 +88,109 @@ TARGET_PATH="minio/obsidian/${SUBPATH}/${FILENAME}"
|
||||
# Ensure subpath ends without trailing slash for mc
|
||||
TARGET_PATH=$(echo "$TARGET_PATH" | sed 's://*:/:g')
|
||||
|
||||
# Check if file already exists
|
||||
if mc stat "$TARGET_PATH" >/dev/null 2>&1; then
|
||||
# User explicitly specified --skip
|
||||
if [ "$SKIP" = true ]; then
|
||||
echo "ℹ️ File already exists, skipping upload: $TARGET_PATH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# User explicitly specified --backup
|
||||
if [ "$BACKUP" = true ]; then
|
||||
BACKUP_PATH="${TARGET_PATH}.bak"
|
||||
echo "📦 Backing up existing file: $TARGET_PATH -> $BACKUP_PATH"
|
||||
mc cp "$TARGET_PATH" "$BACKUP_PATH"
|
||||
# User explicitly specified --rename
|
||||
elif [ "$RENAME" = true ]; then
|
||||
COUNTER=1
|
||||
BASE_NAME="${FILENAME%.*}"
|
||||
EXT="${FILENAME##*.}"
|
||||
|
||||
if [ "$BASE_NAME" = "$EXT" ]; then
|
||||
# No extension
|
||||
BASE_NAME="$FILENAME"
|
||||
EXT=""
|
||||
else
|
||||
EXT=".$EXT"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
NEW_NAME="${BASE_NAME}-${COUNTER}${EXT}"
|
||||
NEW_TARGET="minio/obsidian/${SUBPATH}/${NEW_NAME}"
|
||||
if ! mc stat "$NEW_TARGET" >/dev/null 2>&1; then
|
||||
TARGET_PATH="$NEW_TARGET"
|
||||
FILENAME="$NEW_NAME"
|
||||
echo "📝 Renamed to: $FILENAME"
|
||||
break
|
||||
fi
|
||||
COUNTER=$((COUNTER + 1))
|
||||
done
|
||||
# User explicitly specified --force
|
||||
elif [ "$FORCE" = true ]; then
|
||||
echo "⚠️ Overwriting existing file: $TARGET_PATH"
|
||||
else
|
||||
# No explicit option - prompt user
|
||||
echo ""
|
||||
echo "⚠️ File '$FILENAME' already exists in vault '${SUBPATH}'"
|
||||
echo "Target: $TARGET_PATH"
|
||||
echo ""
|
||||
echo "Choose an action:"
|
||||
echo " [o] Overwrite - Replace existing file"
|
||||
echo " [s] Skip - Cancel upload"
|
||||
echo " [b] Backup - Backup existing file (.bak) then overwrite"
|
||||
echo " [r] Rename - Upload with new name (adds numeric suffix)"
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
read -p "Your choice [o/s/b/r]: " choice
|
||||
case "$choice" in
|
||||
o|O|overwrite)
|
||||
echo "Overwriting existing file..."
|
||||
break
|
||||
;;
|
||||
s|S|skip)
|
||||
echo "❌ Upload cancelled"
|
||||
exit 0
|
||||
;;
|
||||
b|B|backup)
|
||||
BACKUP_PATH="${TARGET_PATH}.bak"
|
||||
echo "📦 Backing up existing file: $TARGET_PATH -> $BACKUP_PATH"
|
||||
mc cp "$TARGET_PATH" "$BACKUP_PATH"
|
||||
break
|
||||
;;
|
||||
r|R|rename)
|
||||
COUNTER=1
|
||||
BASE_NAME="${FILENAME%.*}"
|
||||
EXT="${FILENAME##*.}"
|
||||
|
||||
if [ "$BASE_NAME" = "$EXT" ]; then
|
||||
BASE_NAME="$FILENAME"
|
||||
EXT=""
|
||||
else
|
||||
EXT=".$EXT"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
NEW_NAME="${BASE_NAME}-${COUNTER}${EXT}"
|
||||
NEW_TARGET="minio/obsidian/${SUBPATH}/${NEW_NAME}"
|
||||
if ! mc stat "$NEW_TARGET" >/dev/null 2>&1; then
|
||||
TARGET_PATH="$NEW_TARGET"
|
||||
FILENAME="$NEW_NAME"
|
||||
echo "📝 Renamed to: $FILENAME"
|
||||
break 2
|
||||
fi
|
||||
COUNTER=$((COUNTER + 1))
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "Invalid choice. Please enter o, s, b, or r"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Upload file
|
||||
mc cp "$FILE_PATH" "$TARGET_PATH"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user