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"
|
.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
|
## 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.
|
**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
|
1. **Verify file exists** - Check that the source file is accessible
|
||||||
2. **Determine target vault** - Use specified vault name or default to `main`
|
2. **Determine target vault** - Use specified vault name or default to `main`
|
||||||
3. **Upload with mc** - Execute `mc cp` command to transfer file
|
3. **Check for conflicts** - Detect if file already exists in target vault
|
||||||
4. **Confirm success** - Verify upload completed and report target location
|
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
|
## Error Handling
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,77 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Upload files to MinIO obsidian bucket
|
# Upload files to MinIO obsidian bucket
|
||||||
# Usage: upload_to_obsidian.sh <file_path> [subpath]
|
# Usage: upload_to_obsidian.sh <file_path> [subpath] [options]
|
||||||
#
|
#
|
||||||
# Arguments:
|
# Arguments:
|
||||||
# file_path - Path to the file to upload
|
# 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:
|
# Examples:
|
||||||
# upload_to_obsidian.sh temp/file.md
|
# upload_to_obsidian.sh temp/file.md
|
||||||
# upload_to_obsidian.sh temp/file.md notes/2024
|
# upload_to_obsidian.sh temp/file.md journal
|
||||||
# upload_to_obsidian.sh temp/file.md "archive/old projects"
|
# upload_to_obsidian.sh temp/file.md main --force
|
||||||
|
# upload_to_obsidian.sh temp/file.md work --skip
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Check arguments
|
# Initialize variables
|
||||||
if [ $# -lt 1 ]; then
|
FORCE=false
|
||||||
echo "Usage: $0 <file_path> [subpath]" >&2
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
FILE_PATH="$1"
|
# Check file path argument
|
||||||
SUBPATH="${2:-main}" # Default to 'main' if not specified
|
if [ -z "$FILE_PATH" ]; then
|
||||||
|
echo "Usage: $0 <file_path> [subpath] [--force|--skip|--backup|--rename]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Validate file exists
|
# Validate file exists
|
||||||
if [ ! -f "$FILE_PATH" ]; then
|
if [ ! -f "$FILE_PATH" ]; then
|
||||||
@@ -38,6 +88,109 @@ TARGET_PATH="minio/obsidian/${SUBPATH}/${FILENAME}"
|
|||||||
# Ensure subpath ends without trailing slash for mc
|
# Ensure subpath ends without trailing slash for mc
|
||||||
TARGET_PATH=$(echo "$TARGET_PATH" | sed 's://*:/:g')
|
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
|
# Upload file
|
||||||
mc cp "$FILE_PATH" "$TARGET_PATH"
|
mc cp "$FILE_PATH" "$TARGET_PATH"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user