|
@ECHO OFF |
|
SETLOCAL ENABLEDELAYEDEXPANSION |
|
|
|
REM ── Load .env ──────────────────────────────────────────────────────────────── |
|
SET "_SCRIPT_DIR=%~dp0" |
|
IF EXIST "%_SCRIPT_DIR%.env" ( |
|
FOR /F "usebackq tokens=1,* delims==" %%A IN ("%_SCRIPT_DIR%.env") DO ( |
|
SET "_LINE=%%A" |
|
IF NOT "!_LINE:~0,1!"=="#" IF NOT "!_LINE!"=="" ( |
|
SET "%%A=%%B" |
|
) |
|
) |
|
) |
|
|
|
REM ── Initialise argument variables ───────────────────────────────────────────── |
|
SET "_CMD=" |
|
SET "_FILE=" |
|
SET "_S3PATH=" |
|
SET "_LIST_INLINE=" |
|
|
|
REM ── Parse arguments ─────────────────────────────────────────────────────────── |
|
:parse |
|
IF "%~1"=="" GOTO validate |
|
IF /I "%~1"=="--upload" ( SET "_CMD=upload" & SHIFT & GOTO parse ) |
|
IF /I "%~1"=="--list" GOTO handle_list |
|
IF /I "%~1"=="--delete" ( SET "_CMD=delete" & SHIFT & GOTO parse ) |
|
IF /I "%~1"=="--empty" ( SET "_CMD=empty" & SHIFT & GOTO parse ) |
|
IF /I "%~1"=="--file" ( SET "_FILE=%~2" & SHIFT & SHIFT & GOTO parse ) |
|
IF /I "%~1"=="--s3Path" ( SET "_S3PATH=%~2" & SHIFT & SHIFT & GOTO parse ) |
|
ECHO Unknown argument: %~1 |
|
EXIT /B 1 |
|
|
|
REM --list can take an optional inline s3:// path as the very next token. |
|
REM We must handle this via a label (not a parenthesized block) so that %~1 |
|
REM is re-evaluated AFTER the SHIFT, not captured at block-parse time. |
|
:handle_list |
|
SET "_CMD=list" |
|
SHIFT |
|
SET "_NEXT=%~1" |
|
IF NOT "%_NEXT%"=="" ( |
|
IF NOT "!_NEXT:~0,2!"=="--" ( |
|
SET "_LIST_INLINE=%_NEXT%" |
|
SHIFT |
|
) |
|
) |
|
GOTO parse |
|
|
|
:validate |
|
IF "%_CMD%"=="" ( |
|
ECHO Usage: s3helper.bat --upload ^| --list [S3_PATH] ^| --delete ^| --empty |
|
ECHO [--file PATH] [--s3Path PATH] |
|
EXIT /B 1 |
|
) |
|
|
|
REM ── Validate required env vars ──────────────────────────────────────────────── |
|
IF "%AWS_REGION%"=="" ( ECHO Missing required environment variable: AWS_REGION & EXIT /B 1 ) |
|
IF "%AWS_ACCESS_KEY_ID%"=="" ( ECHO Missing required environment variable: AWS_ACCESS_KEY_ID & EXIT /B 1 ) |
|
IF "%AWS_ACCESS_KEY_SECRET%"="" ( ECHO Missing required environment variable: AWS_ACCESS_KEY_SECRET & EXIT /B 1 ) |
|
IF "%S3_PATH%"=="" ( ECHO Missing required environment variable: S3_PATH & EXIT /B 1 ) |
|
|
|
REM ── Map to the env var names the AWS CLI expects ────────────────────────────── |
|
SET "AWS_SECRET_ACCESS_KEY=%AWS_ACCESS_KEY_SECRET%" |
|
SET "AWS_DEFAULT_REGION=%AWS_REGION%" |
|
|
|
REM ── Dispatch ────────────────────────────────────────────────────────────────── |
|
IF "%_CMD%"=="upload" GOTO cmd_upload |
|
IF "%_CMD%"=="list" GOTO cmd_list |
|
IF "%_CMD%"=="delete" GOTO cmd_delete |
|
IF "%_CMD%"=="empty" GOTO cmd_empty |
|
EXIT /B 1 |
|
|
|
REM ───────────────────────────────────────────────────────────────────────────── |
|
:cmd_upload |
|
IF "%_FILE%"=="" ( ECHO --upload requires --file ^<local_path^> & EXIT /B 1 ) |
|
|
|
REM Extract just the filename (strip directory component) |
|
FOR %%F IN ("%_FILE%") DO SET "_FNAME=%%~nxF" |
|
|
|
REM Resolve destination URI — mirrors resolve_upload_destination() in s3helper.py |
|
IF "%_S3PATH%"=="" ( |
|
CALL :strip_trailing_slash "%S3_PATH%" _BASE |
|
SET "_DEST=!_BASE!/!_FNAME!" |
|
) ELSE ( |
|
CALL :starts_with_s3 "%_S3PATH%" _IS_ABS |
|
IF "!_IS_ABS!"=="1" ( |
|
SET "_LAST=!_S3PATH:~-1!" |
|
IF "!_LAST!"=="/" ( |
|
SET "_DEST=!_S3PATH!!_FNAME!" |
|
) ELSE ( |
|
SET "_DEST=!_S3PATH!" |
|
) |
|
) ELSE ( |
|
CALL :strip_trailing_slash "%S3_PATH%" _BASE |
|
CALL :strip_trailing_slash "%_S3PATH%" _PREFIX |
|
SET "_DEST=!_BASE!/!_PREFIX!/!_FNAME!" |
|
) |
|
) |
|
|
|
aws s3 cp "%_FILE%" "!_DEST!" |
|
IF ERRORLEVEL 1 EXIT /B 1 |
|
ECHO Uploaded: !_DEST! |
|
EXIT /B 0 |
|
|
|
REM ───────────────────────────────────────────────────────────────────────────── |
|
:cmd_list |
|
IF DEFINED _LIST_INLINE ( |
|
SET "_PREFIX_URI=!_LIST_INLINE!" |
|
) ELSE IF NOT "%_S3PATH%"=="" ( |
|
SET "_PREFIX_URI=%_S3PATH%" |
|
) ELSE ( |
|
SET "_PREFIX_URI=%S3_PATH%" |
|
) |
|
|
|
aws s3 ls "!_PREFIX_URI!" --recursive |
|
IF ERRORLEVEL 1 EXIT /B 1 |
|
EXIT /B 0 |
|
|
|
REM ───────────────────────────────────────────────────────────────────────────── |
|
:cmd_delete |
|
IF "%_FILE%"=="" ( ECHO --delete requires --file ^<s3_path_or_relative^> & EXIT /B 1 ) |
|
|
|
CALL :starts_with_s3 "%_FILE%" _IS_ABS |
|
IF "!_IS_ABS!"=="1" ( |
|
SET "_TARGET=%_FILE%" |
|
) ELSE ( |
|
CALL :strip_trailing_slash "%S3_PATH%" _BASE |
|
CALL :strip_leading_slash "%_FILE%" _REL |
|
SET "_TARGET=!_BASE!/!_REL!" |
|
) |
|
|
|
REM Check existence via head-object (mirrors Python's client.head_object call) |
|
CALL :parse_s3_uri "!_TARGET!" _BUCKET _KEY |
|
aws s3api head-object --bucket "!_BUCKET!" --key "!_KEY!" >NUL 2>&1 |
|
IF ERRORLEVEL 1 ( |
|
ECHO Not found: !_TARGET! |
|
EXIT /B 0 |
|
) |
|
aws s3 rm "!_TARGET!" |
|
IF ERRORLEVEL 1 EXIT /B 1 |
|
ECHO Deleted: !_TARGET! |
|
EXIT /B 0 |
|
|
|
REM ───────────────────────────────────────────────────────────────────────────── |
|
:cmd_empty |
|
SET /P "_ANSWER=Empty all objects under %S3_PATH% ? [y/N] " |
|
IF /I NOT "!_ANSWER!"=="y" ( ECHO Aborted. & EXIT /B 0 ) |
|
aws s3 rm "%S3_PATH%" --recursive |
|
IF ERRORLEVEL 1 EXIT /B 1 |
|
EXIT /B 0 |
|
|
|
REM ───────────────────────────────────────────────────────────────────────────── |
|
REM Subroutine: strip trailing / or \ from %~1, store result in var named %~2 |
|
:strip_trailing_slash |
|
SET "_STS=%~1" |
|
IF "!_STS:~-1!"=="/" SET "_STS=!_STS:~0,-1!" |
|
IF "!_STS:~-1!"=="\" SET "_STS=!_STS:~0,-1!" |
|
SET "%~2=!_STS!" |
|
EXIT /B 0 |
|
|
|
REM Subroutine: strip leading / or \ from %~1, store result in var named %~2 |
|
:strip_leading_slash |
|
SET "_SLS=%~1" |
|
IF "!_SLS:~0,1!"=="/" SET "_SLS=!_SLS:~1!" |
|
IF "!_SLS:~0,1!"=="\" SET "_SLS=!_SLS:~1!" |
|
SET "%~2=!_SLS!" |
|
EXIT /B 0 |
|
|
|
REM Subroutine: set %~2=1 if %~1 starts with s3://, else 0 |
|
:starts_with_s3 |
|
SET "_SWS=%~1" |
|
SET "%~2=0" |
|
IF "!_SWS:~0,5!"=="s3://" SET "%~2=1" |
|
EXIT /B 0 |
|
|
|
REM Subroutine: split an s3://bucket/key URI into bucket (%~2) and key (%~3) |
|
:parse_s3_uri |
|
SET "_PSU=%~1" |
|
SET "_PSU=!_PSU:s3://=!" |
|
FOR /F "tokens=1,* delims=/" %%B IN ("!_PSU!") DO ( |
|
SET "%~2=%%B" |
|
SET "%~3=%%C" |
|
) |
|
EXIT /B 0 |