mirror of
https://github.com/FalloutCollaborationProject/FCP-Mod-Updater.git
synced 2026-07-27 17:04:05 -07:00
Merge pull request #1 from FalloutCollaborationProject/update-scripts
Add updating scripts for convenience
This commit is contained in:
@@ -27,11 +27,14 @@ jobs:
|
||||
run: dotnet test --configuration Release
|
||||
|
||||
- name: Build Windows x64 (self-contained)
|
||||
run: dotnet publish FCPModUpdater.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/win-x64-sc
|
||||
run: |
|
||||
dotnet publish FCPModUpdater.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/win-x64-sc
|
||||
cp ./update-fcp-mod-manager.bat ./publish/win-x64-sc/
|
||||
|
||||
- name: Build Linux x64 (self-contained)
|
||||
run: |
|
||||
dotnet publish FCPModUpdater.csproj -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/linux-x64-sc
|
||||
cp ./update-fcp-mod-manager.sh ./publish/linux-x64-sc/
|
||||
cp ./fcp-mod-manager.desktop ./publish/linux-x64-sc/
|
||||
|
||||
- name: Build Windows x64 (framework-dependent)
|
||||
@@ -44,11 +47,14 @@ jobs:
|
||||
|
||||
- name: Create archives
|
||||
run: |
|
||||
cp ./update-fcp-mod-manager.bat ./publish/
|
||||
cp ./update-fcp-mod-manager.sh ./publish/
|
||||
cd ./publish
|
||||
zip -r "${{ env.APP_NAME }}-win-x64-selfcontained.zip" win-x64-sc
|
||||
tar -czvf "${{ env.APP_NAME }}-linux-x64-selfcontained.tar.gz" linux-x64-sc
|
||||
zip -r "${{ env.APP_NAME }}-win-x64.zip" win-x64-fd
|
||||
tar -czvf "${{ env.APP_NAME }}-linux-x64.tar.gz" linux-x64-fd
|
||||
sha256sum "${{ env.APP_NAME }}"-*.zip "${{ env.APP_NAME }}"-*.tar.gz > checksums.txt
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -60,3 +66,6 @@ jobs:
|
||||
./publish/${{ env.APP_NAME }}-linux-x64-selfcontained.tar.gz
|
||||
./publish/${{ env.APP_NAME }}-win-x64.zip
|
||||
./publish/${{ env.APP_NAME }}-linux-x64.tar.gz
|
||||
./publish/checksums.txt
|
||||
./publish/update-fcp-mod-manager.bat
|
||||
./publish/update-fcp-mod-manager.sh
|
||||
|
||||
@@ -38,6 +38,7 @@ public class UpdateCommand(
|
||||
if (updateableMods.Count == 0)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[green]All FCP mods are up to date![/]");
|
||||
await RenderAppUpdateNoticeAsync(updateCheckTask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -57,15 +58,7 @@ public class UpdateCommand(
|
||||
|
||||
ModTableRenderer.RenderUpdateSummary(results);
|
||||
|
||||
UpdateCheckResult? updateResult = await updateCheckTask;
|
||||
if (updateResult != null)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
var label = updateResult.IsPrerelease ? "Pre-release available" : "Update available";
|
||||
AnsiConsole.MarkupLine(
|
||||
$"[yellow bold]{label}: v{updateResult.LatestVersion}[/] [grey](current: {updateResult.CurrentVersion})[/]");
|
||||
AnsiConsole.MarkupLine($"[grey]Download: {updateResult.ReleaseUrl}[/]");
|
||||
}
|
||||
await RenderAppUpdateNoticeAsync(updateCheckTask);
|
||||
|
||||
var failCount = results.Count(r => !r.Success);
|
||||
return failCount > 0 ? 1 : 0;
|
||||
@@ -81,4 +74,14 @@ public class UpdateCommand(
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task RenderAppUpdateNoticeAsync(Task<UpdateCheckResult?> updateCheckTask)
|
||||
{
|
||||
UpdateCheckResult? updateResult = await updateCheckTask;
|
||||
if (updateResult is null)
|
||||
return;
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
AppUpdateNoticeRenderer.Render(updateResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,28 @@
|
||||
|
||||
Downloads are available on the [Releases](https://github.com/FalloutCollaborationProject/FCP-Mod-Updater/releases) page.
|
||||
|
||||
### Updating the App
|
||||
|
||||
FCP Mod Manager checks for newer app releases and prints update instructions when one is available. The recommended update path is still to download the latest self-contained archive from the [Releases](https://github.com/FalloutCollaborationProject/FCP-Mod-Updater/releases) page and replace your existing app folder.
|
||||
|
||||
For convenience, self-contained releases also include manual updater scripts:
|
||||
|
||||
| Platform | Script |
|
||||
|----------|--------|
|
||||
| Windows | `update-fcp-mod-manager.bat` |
|
||||
| Linux | `./update-fcp-mod-manager.sh` |
|
||||
|
||||
Before running an updater script, close FCP Mod Manager and run the script from the existing app folder. The script will ask whether to use the latest stable release or the latest pre-release, download the matching archive and `checksums.txt`, verify the SHA256 checksum, then replace the files in the current folder.
|
||||
|
||||
> [!WARNING]
|
||||
> The updater scripts are mainly convenience helpers. If an update fails, download the release archive manually and replace the app folder yourself.
|
||||
|
||||
> [!WARNING]
|
||||
> The Windows updater script is less tested than the Linux script. If it fails, use the manual download method from the Releases page.
|
||||
|
||||
> [!NOTE]
|
||||
> The Linux updater requires `curl`, `tar`, and `sha256sum`. Choosing the pre-release channel also requires `python3` for GitHub release JSON parsing.
|
||||
|
||||
|
||||
### Build from Source
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using FCPModUpdater.Services;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace FCPModUpdater.UI;
|
||||
|
||||
public static class AppUpdateNoticeRenderer
|
||||
{
|
||||
public static void Render(UpdateCheckResult updateResult)
|
||||
{
|
||||
var label = updateResult.IsPrerelease ? "Pre-release available" : "Update available";
|
||||
var updateCommand = GetUpdateCommand();
|
||||
var content = new Rows(
|
||||
new Markup(
|
||||
$"[yellow bold]{label}: v{Markup.Escape(updateResult.LatestVersion)}[/] [Grey66](current: [/][Grey66 dim bold]{Markup.Escape(updateResult.CurrentVersion)}[/][Grey66])[/]"),
|
||||
new Markup($"[Grey66]Close this app before updating and run:[/] [underline]{Markup.Escape(updateCommand)}[/]"),
|
||||
new Markup($"[Grey66]Manual download:[/] [link]{Markup.Escape(updateResult.ReleaseUrl)}[/]"));
|
||||
|
||||
var panel = new Panel(content)
|
||||
{
|
||||
Border = BoxBorder.Square,
|
||||
BorderStyle = new Style(Color.Grey, null, Decoration.Dim)
|
||||
};
|
||||
panel.Padding = new Padding(1, 0, 1, 0);
|
||||
panel.Expand = false;
|
||||
|
||||
AnsiConsole.Write(panel);
|
||||
}
|
||||
|
||||
private static string GetUpdateCommand()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return @"update-fcp-mod-manager.bat";
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
return "./update-fcp-mod-manager.sh";
|
||||
|
||||
return "download the latest release manually";
|
||||
}
|
||||
}
|
||||
@@ -47,10 +47,7 @@ public class InteractiveMenu
|
||||
var updateResult = await _updateCheckTask;
|
||||
if (updateResult != null)
|
||||
{
|
||||
var label = updateResult.IsPrerelease ? "Pre-release available" : "Update available";
|
||||
AnsiConsole.MarkupLine(
|
||||
$"[yellow bold]{label}: v{updateResult.LatestVersion}[/] [grey](current: {updateResult.CurrentVersion})[/]");
|
||||
AnsiConsole.MarkupLine($"[grey]Download: {updateResult.ReleaseUrl}[/]");
|
||||
AppUpdateNoticeRenderer.Render(updateResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/bin/bash
|
||||
APP_NAME="FCPModUpdater"
|
||||
mkdir -p ./publish
|
||||
cp ./update-fcp-mod-manager.bat ./publish/
|
||||
cp ./update-fcp-mod-manager.sh ./publish/
|
||||
|
||||
# Self-contained
|
||||
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/win-x64-sc ./FCPModUpdater.csproj
|
||||
dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/linux-x64-sc ./FCPModUpdater.csproj
|
||||
cp ./update-fcp-mod-manager.bat ./publish/win-x64-sc/
|
||||
cp ./update-fcp-mod-manager.sh ./publish/linux-x64-sc/
|
||||
cp ./fcp-mod-manager.desktop ./publish/linux-x64-sc/
|
||||
|
||||
# Archive self-contained builds
|
||||
@@ -21,4 +26,5 @@ cp ./fcp-mod-manager.desktop ./publish/linux-x64-fd/
|
||||
cd ./publish
|
||||
zip -r "${APP_NAME}-win-x64.zip" win-x64-fd
|
||||
tar -czvf "${APP_NAME}-linux-x64.tar.gz" linux-x64-fd
|
||||
sha256sum "${APP_NAME}"-*.zip "${APP_NAME}"-*.tar.gz > checksums.txt
|
||||
cd ..
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
if /I not "%~1"=="--from-temp" (
|
||||
set "TEMP_SCRIPT=%TEMP%\update-fcp-mod-manager-%RANDOM%%RANDOM%.bat"
|
||||
copy "%~f0" "%TEMP_SCRIPT%" >nul
|
||||
call "%TEMP_SCRIPT%" --from-temp
|
||||
set "EXIT_CODE=%ERRORLEVEL%"
|
||||
del "%TEMP_SCRIPT%" >nul 2>nul
|
||||
exit /b %EXIT_CODE%
|
||||
)
|
||||
|
||||
set "APP_NAME=FCPModUpdater.exe"
|
||||
set "ARCHIVE=FCPModUpdater-win-x64-selfcontained.zip"
|
||||
set "BASE_URL=https://github.com/FalloutCollaborationProject/FCP-Mod-Updater/releases/latest/download"
|
||||
set "RELEASES_API=https://api.github.com/repos/FalloutCollaborationProject/FCP-Mod-Updater/releases?per_page=50"
|
||||
set "STAGING=%TEMP%\FCPModUpdater-update-%RANDOM%%RANDOM%"
|
||||
|
||||
if not exist ".\%APP_NAME%" (
|
||||
echo Run this script from the existing FCP Mod Manager install folder.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
tasklist /FI "IMAGENAME eq %APP_NAME%" 2>nul | find /I "%APP_NAME%" >nul
|
||||
if "%ERRORLEVEL%"=="0" (
|
||||
echo FCP Mod Manager is still running. Close it before updating.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
mkdir "%STAGING%" >nul 2>nul
|
||||
if not exist "%STAGING%" (
|
||||
echo Could not create update staging folder.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Select update channel:
|
||||
echo 1^) Latest stable release
|
||||
echo 2^) Latest pre-release
|
||||
set /P "CHANNEL_CHOICE=Choice [1]: "
|
||||
if "%CHANNEL_CHOICE%"=="" set "CHANNEL_CHOICE=1"
|
||||
|
||||
if "%CHANNEL_CHOICE%"=="1" (
|
||||
echo Downloading latest stable FCP Mod Manager release...
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri '%BASE_URL%/%ARCHIVE%' -OutFile '%STAGING%\%ARCHIVE%'; Invoke-WebRequest -Uri '%BASE_URL%/checksums.txt' -OutFile '%STAGING%\checksums.txt'"
|
||||
) else if "%CHANNEL_CHOICE%"=="2" (
|
||||
echo Finding latest FCP Mod Manager pre-release...
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; $headers = @{ 'User-Agent' = 'FCPModUpdater' }; $releases = Invoke-RestMethod -Headers $headers -Uri '%RELEASES_API%'; $release = $releases | Where-Object { $_.prerelease -and -not $_.draft } | Select-Object -First 1; if ($null -eq $release) { throw 'No pre-release was found.' }; $archive = $release.assets | Where-Object { $_.name -eq '%ARCHIVE%' } | Select-Object -First 1; $checksums = $release.assets | Where-Object { $_.name -eq 'checksums.txt' } | Select-Object -First 1; if ($null -eq $archive) { throw ('No asset named %ARCHIVE% was found on ' + $release.tag_name + '.') }; if ($null -eq $checksums) { throw ('No checksums.txt asset was found on ' + $release.tag_name + '.') }; Invoke-WebRequest -Headers $headers -Uri $archive.browser_download_url -OutFile '%STAGING%\%ARCHIVE%'; Invoke-WebRequest -Headers $headers -Uri $checksums.browser_download_url -OutFile '%STAGING%\checksums.txt'"
|
||||
) else (
|
||||
echo Invalid selection.
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not "%ERRORLEVEL%"=="0" (
|
||||
echo Download failed.
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set "EXPECTED="
|
||||
for /f "tokens=1" %%A in ('findstr /I /C:" %ARCHIVE%" "%STAGING%\checksums.txt"') do set "EXPECTED=%%A"
|
||||
|
||||
if "%EXPECTED%"=="" (
|
||||
echo Could not find checksum for %ARCHIVE%.
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set "ACTUAL="
|
||||
for /f %%A in ('powershell -NoProfile -ExecutionPolicy Bypass -Command "(Get-FileHash -Algorithm SHA256 '%STAGING%\%ARCHIVE%').Hash.ToLowerInvariant()"') do set "ACTUAL=%%A"
|
||||
|
||||
if /I not "%ACTUAL%"=="%EXPECTED%" (
|
||||
echo Checksum verification failed. Update was not installed.
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "Expand-Archive -Force -Path '%STAGING%\%ARCHIVE%' -DestinationPath '%STAGING%'"
|
||||
if not "%ERRORLEVEL%"=="0" (
|
||||
echo Could not extract update archive.
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist "%STAGING%\win-x64-sc" (
|
||||
echo Downloaded archive did not contain the expected win-x64-sc folder.
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Installing update...
|
||||
robocopy "%STAGING%\win-x64-sc" "%CD%" /E /COPY:DAT /R:3 /W:1 /NFL /NDL
|
||||
set "COPY_EXIT=%ERRORLEVEL%"
|
||||
rmdir /S /Q "%STAGING%" >nul 2>nul
|
||||
|
||||
if %COPY_EXIT% GEQ 8 (
|
||||
echo Update copy failed.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Update complete. Start FCP Mod Manager normally.
|
||||
exit /b 0
|
||||
Executable
+130
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="FCPModUpdater"
|
||||
ARCHIVE="FCPModUpdater-linux-x64-selfcontained.tar.gz"
|
||||
BASE_URL="https://github.com/FalloutCollaborationProject/FCP-Mod-Updater/releases/latest/download"
|
||||
RELEASES_API="https://api.github.com/repos/FalloutCollaborationProject/FCP-Mod-Updater/releases?per_page=50"
|
||||
|
||||
if [[ "${1:-}" != "--from-temp" ]]; then
|
||||
temp_script="$(mktemp)"
|
||||
cp "$0" "${temp_script}"
|
||||
set +e
|
||||
bash "${temp_script}" --from-temp
|
||||
exit_code=$?
|
||||
set -e
|
||||
rm -f "${temp_script}"
|
||||
exit "${exit_code}"
|
||||
fi
|
||||
|
||||
if [[ ! -f "./${APP_NAME}" ]]; then
|
||||
echo "Run this script from the existing FCP Mod Manager install folder."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pgrep -x "${APP_NAME}" >/dev/null 2>&1; then
|
||||
echo "FCP Mod Manager is still running. Close it before updating."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for tool in curl tar sha256sum; do
|
||||
if ! command -v "${tool}" >/dev/null 2>&1; then
|
||||
echo "Missing required tool: ${tool}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -w "." ]]; then
|
||||
echo "This install folder is not writable. Move the app to a writable folder or update manually."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
staging="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "${staging}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "Select update channel:"
|
||||
echo " 1) Latest stable release"
|
||||
echo " 2) Latest pre-release"
|
||||
read -r -p "Choice [1]: " channel_choice
|
||||
|
||||
case "${channel_choice:-1}" in
|
||||
1)
|
||||
echo "Downloading latest stable FCP Mod Manager release..."
|
||||
curl -fsSL "${BASE_URL}/${ARCHIVE}" -o "${staging}/${ARCHIVE}"
|
||||
curl -fsSL "${BASE_URL}/checksums.txt" -o "${staging}/checksums.txt"
|
||||
;;
|
||||
2)
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "Missing required tool for pre-release selection: python3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Finding latest FCP Mod Manager pre-release..."
|
||||
curl -fsSL "${RELEASES_API}" -o "${staging}/releases.json"
|
||||
mapfile -t asset_urls < <(python3 - "${ARCHIVE}" "${staging}/releases.json" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
archive_name = sys.argv[1]
|
||||
releases_path = sys.argv[2]
|
||||
|
||||
with open(releases_path, "r", encoding="utf-8") as handle:
|
||||
releases = json.load(handle)
|
||||
|
||||
release = next((item for item in releases if item.get("prerelease") and not item.get("draft")), None)
|
||||
if release is None:
|
||||
sys.exit("No pre-release was found.")
|
||||
|
||||
assets = release.get("assets", [])
|
||||
archive = next((asset for asset in assets if asset.get("name") == archive_name), None)
|
||||
checksums = next((asset for asset in assets if asset.get("name") == "checksums.txt"), None)
|
||||
|
||||
if archive is None:
|
||||
sys.exit(f"No asset named {archive_name} was found on {release.get('tag_name', 'the pre-release')}.")
|
||||
if checksums is None:
|
||||
sys.exit(f"No checksums.txt asset was found on {release.get('tag_name', 'the pre-release')}.")
|
||||
|
||||
print(archive["browser_download_url"])
|
||||
print(checksums["browser_download_url"])
|
||||
PY
|
||||
)
|
||||
|
||||
if [[ "${#asset_urls[@]}" -ne 2 ]]; then
|
||||
echo "Could not resolve pre-release download URLs."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Downloading latest pre-release FCP Mod Manager release..."
|
||||
curl -fsSL "${asset_urls[0]}" -o "${staging}/${ARCHIVE}"
|
||||
curl -fsSL "${asset_urls[1]}" -o "${staging}/checksums.txt"
|
||||
;;
|
||||
*)
|
||||
echo "Invalid selection."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
cd "${staging}"
|
||||
if ! grep " ${ARCHIVE}$" checksums.txt | sha256sum -c -; then
|
||||
echo "Checksum verification failed. Update was not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -xzf "${ARCHIVE}"
|
||||
source_dir="${staging}/linux-x64-sc"
|
||||
|
||||
if [[ ! -d "${source_dir}" ]]; then
|
||||
echo "Downloaded archive did not contain the expected linux-x64-sc folder."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd - >/dev/null
|
||||
|
||||
echo "Installing update..."
|
||||
cp -R "${source_dir}/." .
|
||||
chmod +x "./${APP_NAME}" "./update-fcp-mod-manager.sh"
|
||||
|
||||
echo "Update complete. Start FCP Mod Manager normally."
|
||||
Reference in New Issue
Block a user