Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions install-eap.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ set "CURRENT_FILE=%JUNIE_DATA%\current"
set "PENDING_UPDATE=%UPDATES_DIR%\pending-update.json"
set "PROCESSING=%UPDATES_DIR%\pending-update.json.processing"

:: Persistent upgrade log (mirrors the POSIX shim). Lives under
:: %USERPROFILE%\.junie\logs -- separate from the data dir so a reinstall that
:: wipes ~/.local/share/junie does not lose update history. Always appended,
:: never truncated. Override the directory with JUNIE_LOG_DIR. A per-invocation
:: session id (LOG_SID) is stamped on every line so one launch can be followed.
set "UPGRADE_LOG_DIR=%USERPROFILE%\.junie\logs"
if defined JUNIE_LOG_DIR set "UPGRADE_LOG_DIR=%JUNIE_LOG_DIR%"
set "UPGRADE_LOG=%UPGRADE_LOG_DIR%\upgrade.log"
set "LOG_SID=%RANDOM%%RANDOM%"
if not exist "%UPGRADE_LOG_DIR%" mkdir "%UPGRADE_LOG_DIR%" 2>nul

:: === Channel Switching (one-shot) ===
::
:: `junie --eap` (also --nightly / --release / --experimental, or --channel=<name>)
Expand Down Expand Up @@ -369,6 +380,7 @@ if exist "%PENDING_UPDATE%" (
for /f "usebackq delims=" %%V in (`powershell -NoProfile -Command "try { $c = Get-Content -Raw -LiteralPath '%PENDING_UPDATE%' -ErrorAction Stop; if ([string]::IsNullOrWhiteSpace($c)) { 'bad' } else { $j = $c | ConvertFrom-Json; if ($j.version -and $j.zipPath) { 'ok' } else { 'bad' } } } catch { 'bad' }"`) do set "PEND_OK=%%V"
if /i not "!PEND_OK!"=="ok" (
echo [Junie] Removing invalid update artifact: %PENDING_UPDATE% 1>&2
call :log_upgrade "sanitize: removing invalid update artifact pending-update.json"
del /f /q "%PENDING_UPDATE%" 2>nul
)
)
Expand All @@ -377,6 +389,7 @@ if exist "!PROCESSING!" (
for /f "usebackq delims=" %%V in (`powershell -NoProfile -Command "try { $c = Get-Content -Raw -LiteralPath '!PROCESSING!' -ErrorAction Stop; if ([string]::IsNullOrWhiteSpace($c)) { 'bad' } else { $j = $c | ConvertFrom-Json; if ($j.version -and $j.zipPath) { 'ok' } else { 'bad' } } } catch { 'bad' }"`) do set "PROC_OK=%%V"
if /i not "!PROC_OK!"=="ok" (
echo [Junie] Removing invalid update artifact: !PROCESSING! 1>&2
call :log_upgrade "sanitize: removing invalid update artifact pending-update.json.processing"
del /f /q "!PROCESSING!" 2>nul
)
)
Expand All @@ -392,39 +405,60 @@ if errorlevel 1 (
goto :resolve_version
)
set "CLAIMED_UPDATE=%UPDATES_DIR%\pending-update.json.processing"
call :log_upgrade "apply: claimed pending update for processing"

:: Parse the claimed manifest using PowerShell (reliable JSON parsing)
for /f "usebackq delims=" %%V in (`powershell -NoProfile -Command "(Get-Content '!CLAIMED_UPDATE!' | ConvertFrom-Json).version"`) do set "UPD_VERSION=%%V"
for /f "usebackq delims=" %%Z in (`powershell -NoProfile -Command "(Get-Content '!CLAIMED_UPDATE!' | ConvertFrom-Json).zipPath"`) do set "UPD_ZIP=%%Z"
for /f "usebackq delims=" %%S in (`powershell -NoProfile -Command "(Get-Content '!CLAIMED_UPDATE!' | ConvertFrom-Json).sha256"`) do set "UPD_SHA256=%%S"
call :log_upgrade "apply: manifest parsed version=!UPD_VERSION! zipPath=!UPD_ZIP! sha256=!UPD_SHA256!"

if not defined UPD_VERSION (
echo [Junie] Invalid pending update manifest, skipping 1>&2
call :log_upgrade "apply: invalid manifest (missing version); dropping manifest"
del /f /q "!CLAIMED_UPDATE!" 2>nul
goto :resolve_version
)
if not defined UPD_ZIP (
echo [Junie] Invalid pending update manifest, skipping 1>&2
call :log_upgrade "apply: invalid manifest (missing zipPath); dropping manifest"
del /f /q "!CLAIMED_UPDATE!" 2>nul
goto :resolve_version
)
if not exist "!UPD_ZIP!" (
echo [Junie] Update file not found: !UPD_ZIP! 1>&2
call :log_upgrade "apply: update archive not found at !UPD_ZIP!; dropping manifest"
del /f /q "!CLAIMED_UPDATE!" 2>nul
goto :resolve_version
)

:: Verify SHA-256 checksum
if defined UPD_SHA256 (
call :log_upgrade "checksum: starting SHA-256 validation expected=!UPD_SHA256!"
set "ACTUAL_SHA="
for /f "usebackq delims=" %%H in (`powershell -NoProfile -Command "(Get-FileHash '!UPD_ZIP!' -Algorithm SHA256).Hash.ToLower()"`) do set "ACTUAL_SHA=%%H"
if not defined ACTUAL_SHA (
:: Get-FileHash produced no output -- the zip could not be read/hashed,
:: almost always because it is still locked or being scanned by antivirus.
:: This is a transient condition, NOT corruption: PRESERVE the manifest and
:: zip and retry on the next launch. Deleting them here (as a plain mismatch
:: would) makes the binary re-download and re-fail every startup -- an
:: endless update loop that never recovers.
echo [Junie] Could not read update zip ^(locked or AV scan?^); will retry next launch 1>&2
call :log_upgrade "checksum: UNREADABLE -- Get-FileHash returned no output; preserving manifest and archive for retry"
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
goto :resolve_version
)
if /i not "!ACTUAL_SHA!"=="!UPD_SHA256!" (
echo [Junie] Checksum mismatch, skipping update 1>&2
echo [Junie] Expected: !UPD_SHA256! 1>&2
echo [Junie] Got: !ACTUAL_SHA! 1>&2
call :log_upgrade "checksum: FAIL expected=!UPD_SHA256! got=!ACTUAL_SHA! -- dropping manifest and archive"
del /f /q "!CLAIMED_UPDATE!" 2>nul
del /f /q "!UPD_ZIP!" 2>nul
goto :resolve_version
)
call :log_upgrade "checksum: OK"
)

:: Extract to a staging dir on the same filesystem as %VERSIONS_DIR%, validate
Expand All @@ -441,9 +475,11 @@ if exist "!UPD_STAGING!" rmdir /s /q "!UPD_STAGING!" 2>nul
mkdir "!UPD_STAGING!" 2>nul

echo [Junie] Applying pending update to version !UPD_VERSION!... 1>&2
call :log_upgrade "extract: starting Expand-Archive src=!UPD_ZIP! dst=!UPD_STAGING!"
powershell -NoProfile -Command "Expand-Archive -Path '!UPD_ZIP!' -DestinationPath '!UPD_STAGING!' -Force" 2>nul
if errorlevel 1 (
echo [Junie] Failed to extract update; preserving for retry 1>&2
call :log_upgrade "extract: FAIL (Expand-Archive non-zero); preserving manifest and archive for retry"
rmdir /s /q "!UPD_STAGING!" 2>nul
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
goto :resolve_version
Expand All @@ -452,6 +488,7 @@ if errorlevel 1 (
:: Validate extracted binary
if not exist "!UPD_STAGING!\junie\junie.exe" (
echo [Junie] Extracted payload missing junie\junie.exe; preserving for retry 1>&2
call :log_upgrade "validate: FAIL (junie\junie.exe missing in payload); preserving manifest and archive for retry"
rmdir /s /q "!UPD_STAGING!" 2>nul
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
goto :resolve_version
Expand All @@ -462,6 +499,7 @@ if exist "!UPD_TARGET!" rename "!UPD_TARGET!" "!UPD_OLD_NAME!" 2>nul
move "!UPD_STAGING!" "!UPD_TARGET!" >nul 2>nul
if errorlevel 1 (
echo [Junie] Failed to install new version; preserving for retry 1>&2
call :log_upgrade "swap: FAIL (could not move staging into place); rolled back and preserving manifest for retry"
if exist "!UPD_OLD!" if not exist "!UPD_TARGET!" rename "!UPD_OLD!" "!UPD_VERSION!" 2>nul
rmdir /s /q "!UPD_STAGING!" 2>nul
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
Expand All @@ -477,6 +515,7 @@ if exist "!UPD_OLD!" rmdir /s /q "!UPD_OLD!" 2>nul
:: Cleanup downloaded zip and manifest only after a successful swap
del /f /q "!UPD_ZIP!" 2>nul
del /f /q "!CLAIMED_UPDATE!" 2>nul
call :log_upgrade "apply: SUCCESS updated to version !UPD_VERSION!"
echo [Junie] Updated to version !UPD_VERSION! 1>&2

:: === Resolve Version ===
Expand Down Expand Up @@ -543,6 +582,18 @@ for %%A in (%*) do (
:: Export this shim's own absolute path (%~f0) so the binary can refresh it in
:: place during auto-update (see ShimUpdater on the binary side).
endlocal & set "EJ_RUNNER_PWD=%EJ_RUNNER_PWD%" & set "JUNIE_DATA=%JUNIE_DATA%" & set "JUNIE_SHIM_PATH=%~f0" & "%JUNIE_EXE%" %FILTERED_ARGS%

:: Jump past the subroutines to end of file, preserving junie.exe's exit code.
goto :eof

:: === Subroutines ===

:log_upgrade
:: Append a timestamped, session-tagged line to the persistent upgrade log.
:: Best-effort: a failure to write (e.g. read-only profile) must never break
:: launching, so the error is swallowed. %~1 is the (already-expanded) message.
echo [%DATE% %TIME%] [sid=%LOG_SID%] %~1>> "%UPGRADE_LOG%" 2>nul
exit /b 0
'@
[System.IO.File]::WriteAllText($SHIM_PATH, $SHIM_CONTENT)

Expand Down
51 changes: 51 additions & 0 deletions install-experimental.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ set "CURRENT_FILE=%JUNIE_DATA%\current"
set "PENDING_UPDATE=%UPDATES_DIR%\pending-update.json"
set "PROCESSING=%UPDATES_DIR%\pending-update.json.processing"

:: Persistent upgrade log (mirrors the POSIX shim). Lives under
:: %USERPROFILE%\.junie\logs -- separate from the data dir so a reinstall that
:: wipes ~/.local/share/junie does not lose update history. Always appended,
:: never truncated. Override the directory with JUNIE_LOG_DIR. A per-invocation
:: session id (LOG_SID) is stamped on every line so one launch can be followed.
set "UPGRADE_LOG_DIR=%USERPROFILE%\.junie\logs"
if defined JUNIE_LOG_DIR set "UPGRADE_LOG_DIR=%JUNIE_LOG_DIR%"
set "UPGRADE_LOG=%UPGRADE_LOG_DIR%\upgrade.log"
set "LOG_SID=%RANDOM%%RANDOM%"
if not exist "%UPGRADE_LOG_DIR%" mkdir "%UPGRADE_LOG_DIR%" 2>nul

:: === Channel Switching (one-shot) ===
::
:: `junie --eap` (also --nightly / --release / --experimental, or --channel=<name>)
Expand Down Expand Up @@ -369,6 +380,7 @@ if exist "%PENDING_UPDATE%" (
for /f "usebackq delims=" %%V in (`powershell -NoProfile -Command "try { $c = Get-Content -Raw -LiteralPath '%PENDING_UPDATE%' -ErrorAction Stop; if ([string]::IsNullOrWhiteSpace($c)) { 'bad' } else { $j = $c | ConvertFrom-Json; if ($j.version -and $j.zipPath) { 'ok' } else { 'bad' } } } catch { 'bad' }"`) do set "PEND_OK=%%V"
if /i not "!PEND_OK!"=="ok" (
echo [Junie] Removing invalid update artifact: %PENDING_UPDATE% 1>&2
call :log_upgrade "sanitize: removing invalid update artifact pending-update.json"
del /f /q "%PENDING_UPDATE%" 2>nul
)
)
Expand All @@ -377,6 +389,7 @@ if exist "!PROCESSING!" (
for /f "usebackq delims=" %%V in (`powershell -NoProfile -Command "try { $c = Get-Content -Raw -LiteralPath '!PROCESSING!' -ErrorAction Stop; if ([string]::IsNullOrWhiteSpace($c)) { 'bad' } else { $j = $c | ConvertFrom-Json; if ($j.version -and $j.zipPath) { 'ok' } else { 'bad' } } } catch { 'bad' }"`) do set "PROC_OK=%%V"
if /i not "!PROC_OK!"=="ok" (
echo [Junie] Removing invalid update artifact: !PROCESSING! 1>&2
call :log_upgrade "sanitize: removing invalid update artifact pending-update.json.processing"
del /f /q "!PROCESSING!" 2>nul
)
)
Expand All @@ -392,39 +405,60 @@ if errorlevel 1 (
goto :resolve_version
)
set "CLAIMED_UPDATE=%UPDATES_DIR%\pending-update.json.processing"
call :log_upgrade "apply: claimed pending update for processing"

:: Parse the claimed manifest using PowerShell (reliable JSON parsing)
for /f "usebackq delims=" %%V in (`powershell -NoProfile -Command "(Get-Content '!CLAIMED_UPDATE!' | ConvertFrom-Json).version"`) do set "UPD_VERSION=%%V"
for /f "usebackq delims=" %%Z in (`powershell -NoProfile -Command "(Get-Content '!CLAIMED_UPDATE!' | ConvertFrom-Json).zipPath"`) do set "UPD_ZIP=%%Z"
for /f "usebackq delims=" %%S in (`powershell -NoProfile -Command "(Get-Content '!CLAIMED_UPDATE!' | ConvertFrom-Json).sha256"`) do set "UPD_SHA256=%%S"
call :log_upgrade "apply: manifest parsed version=!UPD_VERSION! zipPath=!UPD_ZIP! sha256=!UPD_SHA256!"

if not defined UPD_VERSION (
echo [Junie] Invalid pending update manifest, skipping 1>&2
call :log_upgrade "apply: invalid manifest (missing version); dropping manifest"
del /f /q "!CLAIMED_UPDATE!" 2>nul
goto :resolve_version
)
if not defined UPD_ZIP (
echo [Junie] Invalid pending update manifest, skipping 1>&2
call :log_upgrade "apply: invalid manifest (missing zipPath); dropping manifest"
del /f /q "!CLAIMED_UPDATE!" 2>nul
goto :resolve_version
)
if not exist "!UPD_ZIP!" (
echo [Junie] Update file not found: !UPD_ZIP! 1>&2
call :log_upgrade "apply: update archive not found at !UPD_ZIP!; dropping manifest"
del /f /q "!CLAIMED_UPDATE!" 2>nul
goto :resolve_version
)

:: Verify SHA-256 checksum
if defined UPD_SHA256 (
call :log_upgrade "checksum: starting SHA-256 validation expected=!UPD_SHA256!"
set "ACTUAL_SHA="
for /f "usebackq delims=" %%H in (`powershell -NoProfile -Command "(Get-FileHash '!UPD_ZIP!' -Algorithm SHA256).Hash.ToLower()"`) do set "ACTUAL_SHA=%%H"
if not defined ACTUAL_SHA (
:: Get-FileHash produced no output -- the zip could not be read/hashed,
:: almost always because it is still locked or being scanned by antivirus.
:: This is a transient condition, NOT corruption: PRESERVE the manifest and
:: zip and retry on the next launch. Deleting them here (as a plain mismatch
:: would) makes the binary re-download and re-fail every startup -- an
:: endless update loop that never recovers.
echo [Junie] Could not read update zip ^(locked or AV scan?^); will retry next launch 1>&2
call :log_upgrade "checksum: UNREADABLE -- Get-FileHash returned no output; preserving manifest and archive for retry"
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
goto :resolve_version
)
if /i not "!ACTUAL_SHA!"=="!UPD_SHA256!" (
echo [Junie] Checksum mismatch, skipping update 1>&2
echo [Junie] Expected: !UPD_SHA256! 1>&2
echo [Junie] Got: !ACTUAL_SHA! 1>&2
call :log_upgrade "checksum: FAIL expected=!UPD_SHA256! got=!ACTUAL_SHA! -- dropping manifest and archive"
del /f /q "!CLAIMED_UPDATE!" 2>nul
del /f /q "!UPD_ZIP!" 2>nul
goto :resolve_version
)
call :log_upgrade "checksum: OK"
)

:: Extract to a staging dir on the same filesystem as %VERSIONS_DIR%, validate
Expand All @@ -441,9 +475,11 @@ if exist "!UPD_STAGING!" rmdir /s /q "!UPD_STAGING!" 2>nul
mkdir "!UPD_STAGING!" 2>nul

echo [Junie] Applying pending update to version !UPD_VERSION!... 1>&2
call :log_upgrade "extract: starting Expand-Archive src=!UPD_ZIP! dst=!UPD_STAGING!"
powershell -NoProfile -Command "Expand-Archive -Path '!UPD_ZIP!' -DestinationPath '!UPD_STAGING!' -Force" 2>nul
if errorlevel 1 (
echo [Junie] Failed to extract update; preserving for retry 1>&2
call :log_upgrade "extract: FAIL (Expand-Archive non-zero); preserving manifest and archive for retry"
rmdir /s /q "!UPD_STAGING!" 2>nul
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
goto :resolve_version
Expand All @@ -452,6 +488,7 @@ if errorlevel 1 (
:: Validate extracted binary
if not exist "!UPD_STAGING!\junie\junie.exe" (
echo [Junie] Extracted payload missing junie\junie.exe; preserving for retry 1>&2
call :log_upgrade "validate: FAIL (junie\junie.exe missing in payload); preserving manifest and archive for retry"
rmdir /s /q "!UPD_STAGING!" 2>nul
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
goto :resolve_version
Expand All @@ -462,6 +499,7 @@ if exist "!UPD_TARGET!" rename "!UPD_TARGET!" "!UPD_OLD_NAME!" 2>nul
move "!UPD_STAGING!" "!UPD_TARGET!" >nul 2>nul
if errorlevel 1 (
echo [Junie] Failed to install new version; preserving for retry 1>&2
call :log_upgrade "swap: FAIL (could not move staging into place); rolled back and preserving manifest for retry"
if exist "!UPD_OLD!" if not exist "!UPD_TARGET!" rename "!UPD_OLD!" "!UPD_VERSION!" 2>nul
rmdir /s /q "!UPD_STAGING!" 2>nul
rename "!CLAIMED_UPDATE!" "pending-update.json" 2>nul
Expand All @@ -477,6 +515,7 @@ if exist "!UPD_OLD!" rmdir /s /q "!UPD_OLD!" 2>nul
:: Cleanup downloaded zip and manifest only after a successful swap
del /f /q "!UPD_ZIP!" 2>nul
del /f /q "!CLAIMED_UPDATE!" 2>nul
call :log_upgrade "apply: SUCCESS updated to version !UPD_VERSION!"
echo [Junie] Updated to version !UPD_VERSION! 1>&2

:: === Resolve Version ===
Expand Down Expand Up @@ -543,6 +582,18 @@ for %%A in (%*) do (
:: Export this shim's own absolute path (%~f0) so the binary can refresh it in
:: place during auto-update (see ShimUpdater on the binary side).
endlocal & set "EJ_RUNNER_PWD=%EJ_RUNNER_PWD%" & set "JUNIE_DATA=%JUNIE_DATA%" & set "JUNIE_SHIM_PATH=%~f0" & "%JUNIE_EXE%" %FILTERED_ARGS%

:: Jump past the subroutines to end of file, preserving junie.exe's exit code.
goto :eof

:: === Subroutines ===

:log_upgrade
:: Append a timestamped, session-tagged line to the persistent upgrade log.
:: Best-effort: a failure to write (e.g. read-only profile) must never break
:: launching, so the error is swallowed. %~1 is the (already-expanded) message.
echo [%DATE% %TIME%] [sid=%LOG_SID%] %~1>> "%UPGRADE_LOG%" 2>nul
exit /b 0
'@
[System.IO.File]::WriteAllText($SHIM_PATH, $SHIM_CONTENT)

Expand Down
Loading