diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
new file mode 100644
index 0000000..b1d38b0
--- /dev/null
+++ b/.github/workflows/integration-test.yml
@@ -0,0 +1,43 @@
+name: Update Integration Test
+
+on:
+ workflow_call:
+ workflow_dispatch:
+
+jobs:
+ update-cycle:
+ name: Update cycle (${{ matrix.scenario.name }})
+ runs-on: windows-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ scenario:
+ - { name: single, script: ./test-local-update.ps1 }
+ - { name: dual, script: ./test-local-update-dual.ps1 }
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Run ${{ matrix.scenario.name }}-channel update cycle
+ shell: pwsh
+ run: ${{ matrix.scenario.script }}
+
+ - name: Upload diagnostics on failure
+ if: failure()
+ uses: actions/upload-artifact@v7
+ with:
+ name: update-cycle-${{ matrix.scenario.name }}-logs
+ # Updater log: %TEMP%\extUpdateLog-*.txt; RawDevLauncher app log: %APPDATA%\RawDevLauncher\*.txt
+ path: |
+ ${{ runner.temp }}\extUpdateLog-*.txt
+ ${{ env.APPDATA }}\RawDevLauncher\*.txt
+ if-no-files-found: ignore
+ retention-days: 7
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 497ece0..5a9a987 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,84 +13,114 @@ on:
env:
TOOL_PROJ_PATH: ./src/DevLauncher/DevLauncher.csproj
- CREATOR_PROJ_PATH: ./ModdingToolBase/src/AnakinApps/ApplicationManifestCreator/ApplicationManifestCreator.csproj
- UPLOADER_PROJ_PATH: ./ModdingToolBase/src/AnakinApps/FtpUploader/FtpUploader.csproj
+ PUBLISH_SCRIPT: ./ModdingToolBase/scripts/Publish-Release.ps1
TOOL_EXE: RaW-DevLauncher.exe
UPDATER_EXE: AnakinRaW.ExternalUpdater.exe
- MANIFEST_CREATOR: AnakinRaW.ApplicationManifestCreator.dll
- SFTP_UPLOADER: AnakinRaW.FtpUploader.dll
+ EMBEDDED_TRUST_CERT: src/DevLauncher/Resources/Certs/anakinraw-trust.cer
ORIGIN_BASE: https://republicatwar.com/downloads/RawDevLauncher
- ORIGIN_BASE_PART: downloads/RawDevLauncher/
+ SFTP_BASE_PATH: downloads/RawDevLauncher/
BRANCH_NAME: ${{ github.event.inputs.branch || 'stable' }}
+ # Migration-release values. Leave empty for a normal release; populate to enable.
+ #
+ # Origin URL of the next-generation channel, written into the manifest's componentOriginInfo.
+ NEXT_ORIGIN_BASE: https://republicatwar.com/downloads/RawDevLauncher/v2
+
+ # SFTP path the next-generation channel uploads to. Set together with NEXT_ORIGIN_BASE.
+ NEXT_SFTP_BASE_PATH: downloads/RawDevLauncher/v2/
+
+ # Previously-deployed updater used in place of the build-output one for the primary deploy.
+ # Only the old-gen manifest lists this binary; the next-gen manifest still uses the build-output updater.
+ # Requires NEXT_ORIGIN_BASE + NEXT_SFTP_BASE_PATH to be set.
+ COMPAT_UPDATER: tools/v1/AnakinRaW.ExternalUpdater.exe
+
jobs:
- build:
- name: Build
+# Builds and tests the solution.
+ test:
+ uses: ./.github/workflows/test.yml
+
+ # End-to-end self-update test
+ integration-test:
+ needs: [test]
+ uses: ./.github/workflows/integration-test.yml
+
+ pack:
+ name: Pack
+ needs: [test]
runs-on: windows-latest
steps:
- name: Checkout sources
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 9.0.x
- - name: Build
- run: dotnet build ${{ env.TOOL_PROJ_PATH }} --configuration Release --output ./binaries
+ uses: actions/setup-dotnet@v5
+ - name: Create NetFramework Release
+ # use build for .NET Framework to enusre external updater exe is included
+ run: dotnet build ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net481 --output ./releases/net481 /p:DebugType=None /p:DebugSymbols=false
- name: Upload a Build Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
- name: Build Artifacts
- path: |
- binaries/${{env.TOOL_EXE}}
- binaries/${{env.UPDATER_EXE}}
+ name: Binary Releases
+ path: ./releases
+ if-no-files-found: error
retention-days: 1
deploy:
name: Deploy
- # Only deploy on push to main or manual trigger
+ # Stable deploys are gated to 'main'. Non-stable channels (beta, canary, etc.) can be
+ # workflow_dispatched from any branch.
if: |
- (github.ref == 'refs/heads/main' && github.event_name == 'push') || github.event_name == 'workflow_dispatch'
- needs: [build]
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
+ (github.event_name == 'workflow_dispatch' &&
+ (github.event.inputs.branch != 'stable' || github.ref == 'refs/heads/main'))
+ needs: [pack, integration-test]
runs-on: ubuntu-latest
steps:
- name: Checkout sources
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
+ - uses: actions/download-artifact@v8
with:
- dotnet-version: 9.0.x
- - uses: actions/download-artifact@v4
+ name: Binary Releases
+ path: ./releases
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
with:
- name: Build Artifacts
- path: binaries
- - name: Build Creator
- run: dotnet build ${{env.CREATOR_PROJ_PATH}} --configuration Release --output ./dev
- - name: Build Uploader
- run: dotnet build ${{env.UPLOADER_PROJ_PATH}} --configuration Release --output ./dev
- - name: Create Manifest
- run: dotnet ./dev/${{env.MANIFEST_CREATOR}} -a binaries/${{env.TOOL_EXE}} --appDataFiles binaries/${{env.UPDATER_EXE}} --origin ${{env.ORIGIN_BASE}} -o ./binaries -b ${{env.BRANCH_NAME}}
- - name: Upload Build
- run: dotnet ./dev/${{env.SFTP_UPLOADER}} ftp --host $host --port $port -u ${{secrets.SFTP_USER}} -p ${{secrets.SFTP_PASSWORD}} --base $base_path -s $source
- env:
- host: republicatwar.com
- port: 1579
- base_path: ${{env.ORIGIN_BASE_PART}}
- source: ./binaries
+ dotnet-version: 10.0.x
+
+
+ - name: Publish self-update release
+ shell: pwsh
+ run: |
+ & $env:PUBLISH_SCRIPT `
+ -AppExePath "./releases/net481/$env:TOOL_EXE" `
+ -UpdaterExePath "./releases/net481/$env:UPDATER_EXE" `
+ -EmbeddedTrustCertPath "$env:EMBEDDED_TRUST_CERT" `
+ -Origin "$env:ORIGIN_BASE" `
+ -SftpBasePath "$env:SFTP_BASE_PATH" `
+ -Branch "$env:BRANCH_NAME" `
+ -SigningPfxBase64 "${{ secrets.UPDATER_SIGNING_PFX_B64 }}" `
+ -SigningPfxPassword "${{ secrets.UPDATER_SIGNING_PFX_PASSWORD }}" `
+ -SftpHost "republicatwar.com" `
+ -SftpPort 1579 `
+ -SftpUser "${{ secrets.SFTP_USER }}" `
+ -SftpPassword "${{ secrets.SFTP_PASSWORD }}" `
+ -NextOrigin "$env:NEXT_ORIGIN_BASE" `
+ -NextSftpBasePath "$env:NEXT_SFTP_BASE_PATH" `
+ -CompatibilityUpdaterExePath "$env:COMPAT_UPDATER"
- - uses: dotnet/nbgv@v0.4.2
+ - uses: dotnet/nbgv@v0.5.1
id: nbgv
- name: Create GitHub release
# Only create a release on push to main
if: |
github.ref == 'refs/heads/main' && github.event_name == 'push'
- uses: softprops/action-gh-release@v2
+ uses: softprops/action-gh-release@v3
with:
name: v${{ steps.nbgv.outputs.SemVer2 }}
tag_name: v${{ steps.nbgv.outputs.SemVer2 }}
diff --git a/.github/workflows/build.yml b/.github/workflows/test.yml
similarity index 67%
rename from .github/workflows/build.yml
rename to .github/workflows/test.yml
index 1996673..05f2035 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/test.yml
@@ -14,13 +14,13 @@ jobs:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- - uses: actions/setup-dotnet@v4
+ - uses: actions/setup-dotnet@v5
with:
- dotnet-version: 9.0.x
+ dotnet-version: 10.0.x
- name: Build in Release Mode
- run: dotnet build --configuration Release
\ No newline at end of file
+ run: dotnet test --configuration Release --report-github
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 5af3e6f..95991ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -398,4 +398,6 @@ FodyWeavers.xsd
*.sln.iml
-dev/*
\ No newline at end of file
+dev/*
+
+.local_deploy/
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index f9cd8ce..dacff77 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,9 +1,10 @@
-
-
- all
- 3.9.50
-
-
+
+
+
+ all
+ 3.9.50
+
+
\ No newline at end of file
diff --git a/ModVerify b/ModVerify
index 4eb6a1c..a3cb110 160000
--- a/ModVerify
+++ b/ModVerify
@@ -1 +1 @@
-Subproject commit 4eb6a1c0567b68c6a0f858426c478c7572f4d81c
+Subproject commit a3cb110eaa861778284eddcef8806883eb4711b7
diff --git a/ModdingToolBase b/ModdingToolBase
index e12f6ce..49dcf5f 160000
--- a/ModdingToolBase
+++ b/ModdingToolBase
@@ -1 +1 @@
-Subproject commit e12f6ceedb83fe9e3372dd89c68d508f8479cf92
+Subproject commit 49dcf5f0ca7bd4bdd1cc1e2d32655afa906342cb
diff --git a/RawDevTools.sln b/RawDevTools.sln
index 6f39e92..40f2000 100644
--- a/RawDevTools.sln
+++ b/RawDevTools.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
-VisualStudioVersion = 18.4.11519.219 insiders
+VisualStudioVersion = 18.4.11519.219
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevLauncher", "src\DevLauncher\DevLauncher.csproj", "{1BA491BC-2CD6-4270-9573-5FF9529D8D89}"
EndProject
@@ -57,6 +57,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework.Attribu
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ApplicationBase.Shared", "ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.shproj", "{B297A13A-8E3A-436C-BA97-8B5F57827FFE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Engine.FileSystem", "ModVerify\src\PetroglyphTools\PG.StarWarsGame.Engine.FileSystem\PG.StarWarsGame.Engine.FileSystem.csproj", "{679DA7CA-B23B-7160-B6CD-420BD9CD2312}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -139,6 +141,10 @@ Global
{1AA3A6D5-5492-26C3-E36C-54AB5997D2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AA3A6D5-5492-26C3-E36C-54AB5997D2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AA3A6D5-5492-26C3-E36C-54AB5997D2CF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {679DA7CA-B23B-7160-B6CD-420BD9CD2312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {679DA7CA-B23B-7160-B6CD-420BD9CD2312}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {679DA7CA-B23B-7160-B6CD-420BD9CD2312}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {679DA7CA-B23B-7160-B6CD-420BD9CD2312}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -164,6 +170,7 @@ Global
{8768819D-B0C2-4487-3B3A-84A90F36BAB7} = {46DB413A-0F73-48A6-9071-9C38916BE6FC}
{1AA3A6D5-5492-26C3-E36C-54AB5997D2CF} = {3E986062-E81F-4833-A127-24FA73FBCB1B}
{B297A13A-8E3A-436C-BA97-8B5F57827FFE} = {46DB413A-0F73-48A6-9071-9C38916BE6FC}
+ {679DA7CA-B23B-7160-B6CD-420BD9CD2312} = {FF4C3704-0C74-40B5-A38D-AF29B6385D85}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC15751A-BB36-4EF1-BFF0-2DF1E419AC8F}
diff --git a/deploy-local.ps1 b/deploy-local.ps1
new file mode 100644
index 0000000..ca2d090
--- /dev/null
+++ b/deploy-local.ps1
@@ -0,0 +1,49 @@
+param(
+ [string]$InstalledVersion = "0.0.1-local",
+ [string]$ServerVersion = "99.99.99-local",
+ [switch]$DualPublish,
+ [string]$CompatibilityUpdater
+)
+
+$ErrorActionPreference = "Stop"
+
+$root = $PSScriptRoot
+if ([string]::IsNullOrEmpty($root)) { $root = Get-Location }
+
+. (Join-Path $root "ModdingToolBase\scripts\NbgvVersion.ps1")
+
+$deployRoot = Join-Path $root ".local_deploy"
+$installBuildDir = Join-Path $deployRoot "bin\install"
+$serverBuildDir = Join-Path $deployRoot "bin\tool"
+
+$toolProj = Join-Path $root "src\DevLauncher\DevLauncher.csproj"
+$baseScript = Join-Path $root "ModdingToolBase\scripts\Publish-LocalRelease.ps1"
+
+if (Test-Path $deployRoot) { Remove-Item -Recurse -Force $deployRoot }
+New-Item -ItemType Directory -Path $deployRoot | Out-Null
+
+$nbgv = Backup-NbgvVersion -RepoRoot $root
+try {
+ Write-Host "--- Building DevLauncher (net481) @ installed v$InstalledVersion ---" -ForegroundColor Cyan
+ Set-NbgvVersion -Snapshot $nbgv -Version $InstalledVersion
+ dotnet build $toolProj --configuration Release -f net481 --output $installBuildDir /p:DebugType=None /p:DebugSymbols=false /p:LocalDeploy=true
+
+ Write-Host "--- Building DevLauncher (net481) @ server v$ServerVersion ---" -ForegroundColor Cyan
+ Set-NbgvVersion -Snapshot $nbgv -Version $ServerVersion
+ dotnet build $toolProj --configuration Release -f net481 --output $serverBuildDir /p:DebugType=None /p:DebugSymbols=false /p:LocalDeploy=true
+
+ $publishParams = @{
+ AppExePath = Join-Path $serverBuildDir "RaW-DevLauncher.exe"
+ UpdaterExePath = Join-Path $serverBuildDir "AnakinRaW.ExternalUpdater.exe"
+ DeployRoot = $deployRoot
+ InstallBuildDir = $installBuildDir
+ Branch = "beta"
+ }
+ if ($DualPublish) { $publishParams.DualPublish = $true }
+ if ($CompatibilityUpdater) { $publishParams.CompatibilityUpdater = $CompatibilityUpdater }
+
+ & $baseScript @publishParams
+}
+finally {
+ Restore-NbgvVersion -Snapshot $nbgv
+}
\ No newline at end of file
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..802ab21
--- /dev/null
+++ b/global.json
@@ -0,0 +1,5 @@
+{
+ "test": {
+ "runner": "Microsoft.Testing.Platform"
+ }
+}
\ No newline at end of file
diff --git a/src/DevLauncher.Tests/DevLauncher.Tests.csproj b/src/DevLauncher.Tests/DevLauncher.Tests.csproj
index a54378b..bec0165 100644
--- a/src/DevLauncher.Tests/DevLauncher.Tests.csproj
+++ b/src/DevLauncher.Tests/DevLauncher.Tests.csproj
@@ -1,23 +1,28 @@
-
+
- net9.0
+ net481
disable
enable
+
+
false
- true
+ true
+ Exe
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/DevLauncher/DevLauncher.csproj b/src/DevLauncher/DevLauncher.csproj
index 6f7f992..e249ea2 100644
--- a/src/DevLauncher/DevLauncher.csproj
+++ b/src/DevLauncher/DevLauncher.csproj
@@ -18,27 +18,37 @@
true
+
+
+ $(DefineConstants);LOCAL_DEPLOY
+
+
+
+
+
+
-
-
+
+
-
+
-
+
all
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -46,15 +56,15 @@
-
+
compile
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
compile
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
@@ -66,14 +76,6 @@
-
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
-
diff --git a/src/DevLauncher/DevLauncherEnvironment.cs b/src/DevLauncher/DevLauncherEnvironment.cs
index 76dc6d5..fbf8865 100644
--- a/src/DevLauncher/DevLauncherEnvironment.cs
+++ b/src/DevLauncher/DevLauncherEnvironment.cs
@@ -1,9 +1,12 @@
-using System;
+using AnakinRaW.ApplicationBase.Environment;
+using AnakinRaW.AppUpdaterFramework.Configuration;
+using AnakinRaW.AppUpdaterFramework.Security;
+using AnakinRaW.CommonUtilities.DownloadManager.Configuration;
+using System;
using System.Collections.Generic;
using System.IO.Abstractions;
+using System.Net;
using System.Reflection;
-using AnakinRaW.ApplicationBase.Environment;
-using AnakinRaW.AppUpdaterFramework.Configuration;
namespace RepublicAtWar.DevLauncher;
@@ -16,12 +19,20 @@ internal class DevLauncherEnvironment(Assembly assembly, IFileSystem fileSystem)
public override ICollection UpdateMirrors { get; } = new List
{
- new($"https://republicatwar.com/downloads/{ToolPathName}")
+ new($"https://republicatwar.com/downloads/{ToolPathName}/v2")
};
public override string UpdateRegistryPath => $@"SOFTWARE\{ToolPathName}\Update";
protected override string ApplicationLocalDirectoryName => ToolPathName;
+ static DevLauncherEnvironment()
+ {
+ // For some unknown reason, packaging dependencies into the app, may alter the used security protocols...
+ // This reverts the changes and forces secure settings
+ if (ServicePointManager.SecurityProtocol != SecurityProtocolType.SystemDefault)
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault | SecurityProtocolType.Tls12;
+ }
+
protected override UpdateConfiguration CreateUpdateConfiguration()
{
return new()
@@ -34,6 +45,20 @@ protected override UpdateConfiguration CreateUpdateConfiguration()
{
SupportsRestart = true,
PassCurrentArgumentsForRestart = true
+ },
+ ManifestDownloadConfiguration = new ManifestDownloadConfiguration
+ {
+ DownloadRetryDelay = 500
+ },
+ ComponentDownloadConfiguration = new DownloadManagerConfiguration
+ {
+ ValidationPolicy = ValidationPolicy.Required
+ },
+ ValidateInstallation = true,
+ ManifestSigningConfiguration = new SigningConfiguration
+ {
+ Policy = SignaturePolicy.Required,
+ SignatureAlgorithm = SignatureAlgorithm.ES256
}
};
}
diff --git a/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs b/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs
index 1936d5b..cc82138 100644
--- a/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs
+++ b/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs
@@ -1,5 +1,4 @@
-using AET.Modinfo.Model;
-using AnakinRaW.CommonUtilities.SimplePipeline;
+using AnakinRaW.CommonUtilities.SimplePipeline;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
diff --git a/src/DevLauncher/Program.cs b/src/DevLauncher/Program.cs
index 50944ba..71199b2 100644
--- a/src/DevLauncher/Program.cs
+++ b/src/DevLauncher/Program.cs
@@ -14,7 +14,6 @@
using Microsoft.Extensions.Logging;
using PG.Commons;
using PG.StarWarsGame.Engine;
-using PG.StarWarsGame.Engine.Xml.Parsers;
using PG.StarWarsGame.Files.ALO;
using PG.StarWarsGame.Files.DAT;
using PG.StarWarsGame.Files.MEG;
@@ -35,6 +34,7 @@
using System.IO.Abstractions;
using System.Reflection;
using System.Threading.Tasks;
+using PG.StarWarsGame.Engine.Xml;
using Testably.Abstractions;
using ILogger = Serilog.ILogger;
@@ -52,8 +52,10 @@ public static Task Main(string[] args)
internal class Program : SelfUpdateableAppLifecycle
{
- private static readonly string EngineParserNamespace = typeof(XmlObjectParser<>).Namespace!;
- private static readonly string ParserNamespace = typeof(PetroglyphXmlFileParser<>).Namespace!;
+ private const string EmbeddedTrustCertResource = "RaW-DevLauncher.Resources.Certs.anakinraw-trust.cer";
+
+ private static readonly string EngineParserNamespace = typeof(PetroglyphStarWarsGameXmlParser).Namespace!;
+ private static readonly string ParserNamespace = typeof(XmlFileParser<>).Namespace!;
private static readonly string DevLauncherRootNamespace = typeof(Program).Namespace!;
private static readonly string DevLauncherUpdateNamespace = typeof(RawDevLauncherUpdater).Namespace!;
@@ -99,13 +101,32 @@ private async Task RunAppCoreAsync(string[] args, IServiceProvider appServi
{
Log.CloseAndFlush();
- Console.WriteLine();
- ConsoleUtilities.WriteHorizontalLine('-');
- Console.Write("Press ENTER to exit.");
- Console.ReadLine();
+ // Skip the interactive prompt in plain update mode: the host is being driven
+ // by the external updater / a test harness and there is no human at the console.
+ if (!RawDevLauncher.IsUpdateOnlyInvocation(args))
+ {
+ Console.WriteLine();
+ ConsoleUtilities.WriteHorizontalLine('-');
+ Console.Write("Press ENTER to exit.");
+ Console.ReadLine();
+ }
}
}
+ protected override void RegisterTrustedCertificates(IServiceProvider appServices)
+ {
+ if (!IsUpdateableApplication)
+ return;
+
+ string? devCertPath = null;
+#if DEBUG || LOCAL_DEPLOY
+ devCertPath = System.IO.Path.GetFullPath(
+ System.IO.Path.Combine(AppContext.BaseDirectory, "..", "dev-trust.cer"));
+#endif
+ appServices.GetRequiredService()
+ .RegisterTrustedCertificates(typeof(Program).Assembly, [EmbeddedTrustCertResource], devCertPath);
+ }
+
protected override void ResetApp()
{
Logger?.LogDebug("Resetting Application...");
diff --git a/src/DevLauncher/Properties/launchSettings.json b/src/DevLauncher/Properties/launchSettings.json
index e517f5d..cbe0893 100644
--- a/src/DevLauncher/Properties/launchSettings.json
+++ b/src/DevLauncher/Properties/launchSettings.json
@@ -5,11 +5,11 @@
"commandLineArgs": "--verboseBootstrapLogging -verbose",
"workingDirectory": "C:\\Privat\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\republic-at-war"
},
- "DAT2LocFile": {
- "commandName": "Project",
- "commandLineArgs": "initLoc --skipUpdate",
- "workingDirectory": "C:\\Privat\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\republic-at-war"
- },
+ //"DAT2LocFile": {
+ // "commandName": "Project",
+ // "commandLineArgs": "initLoc --skipUpdate",
+ // "workingDirectory": "C:\\Privat\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\republic-at-war"
+ //},
"Diff Localizations": {
"commandName": "Project",
"commandLineArgs": "prepareLoc --skipUpdate",
diff --git a/src/DevLauncher/RawDevLauncher.cs b/src/DevLauncher/RawDevLauncher.cs
index f74e6fb..fd7f3a1 100644
--- a/src/DevLauncher/RawDevLauncher.cs
+++ b/src/DevLauncher/RawDevLauncher.cs
@@ -16,6 +16,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using AnakinRaW.AppUpdaterFramework.Metadata.Product;
namespace RepublicAtWar.DevLauncher;
@@ -25,10 +26,25 @@ internal sealed class RawDevLauncher(UpdatableApplicationEnvironment application
private readonly Parser _looseArgumentParser = new(c => { c.IgnoreUnknownArguments = true; });
private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(RawDevLauncher));
+ // Verb name from ApplicationUpdateOptions in ModdingToolBase; kept as a literal to avoid
+ // coupling Program's finally block to a reference type just for this check.
+ internal const string UpdateApplicationVerb = "updateApplication";
+
+ internal static bool IsUpdateOnlyInvocation(IReadOnlyList args)
+ {
+ return args.Count > 0 && string.Equals(args[0], UpdateApplicationVerb, StringComparison.Ordinal);
+ }
+
public async Task RunAsync(IReadOnlyList args)
{
+ if (IsUpdateOnlyInvocation(args))
+ {
+ await UpdateLauncher(args).ConfigureAwait(false);
+ return 0;
+ }
+
var option = ParseArguments(args);
-
+
if (option is null)
return 0xA0;
@@ -50,7 +66,16 @@ private async Task UpdateLauncher(IReadOnlyList args)
var updater = new RawDevLauncherUpdater(applicationEnvironment, serviceProvider);
var branchName = updater.GetBranchNameFromRegistry(options?.BranchName, true);
- var branch = updater.CreateBranch(branchName, options?.ManifestUrl);
+
+ ProductBranch branch;
+ if (options is not null)
+ {
+ branch = !string.IsNullOrEmpty(options.ServerUrl)
+ ? updater.CreateBranchFromServerUrl(options.ServerUrl!, branchName)
+ : updater.CreateBranch(branchName, options.ManifestUrl);
+ }
+ else
+ branch = updater.CreateBranch(branchName);
await updater.AutoUpdateApplication(branch);
}
diff --git a/src/DevLauncher/Resources/Certs/anakinraw-trust.cer b/src/DevLauncher/Resources/Certs/anakinraw-trust.cer
new file mode 100644
index 0000000..f6fcda4
Binary files /dev/null and b/src/DevLauncher/Resources/Certs/anakinraw-trust.cer differ
diff --git a/src/MegCompile/MegCompile.csproj b/src/MegCompile/MegCompile.csproj
index b319200..68bce03 100644
--- a/src/MegCompile/MegCompile.csproj
+++ b/src/MegCompile/MegCompile.csproj
@@ -16,18 +16,18 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/src/RawDevTools/RawDevTools.csproj b/src/RawDevTools/RawDevTools.csproj
index b47feff..bfb86b4 100644
--- a/src/RawDevTools/RawDevTools.csproj
+++ b/src/RawDevTools/RawDevTools.csproj
@@ -43,13 +43,13 @@
-
+
-
-
-
-
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/TextCompile/TextCompile.csproj b/src/TextCompile/TextCompile.csproj
index e060ac5..90c7ad5 100644
--- a/src/TextCompile/TextCompile.csproj
+++ b/src/TextCompile/TextCompile.csproj
@@ -15,18 +15,18 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/test-local-update-dual.ps1 b/test-local-update-dual.ps1
new file mode 100644
index 0000000..57ae3a8
--- /dev/null
+++ b/test-local-update-dual.ps1
@@ -0,0 +1,39 @@
+#Requires -Version 7.0
+
+[CmdletBinding()]
+param(
+ [string]$InstalledVersion = '0.0.1-local',
+ [string]$ServerVersion = '99.99.99-local',
+ [string]$Branch = 'beta',
+ [string]$CompatibilityUpdater
+)
+
+$ErrorActionPreference = 'Stop'
+
+$root = $PSScriptRoot
+if ([string]::IsNullOrEmpty($root)) { $root = Get-Location }
+
+$deployArgs = @{
+ InstalledVersion = $InstalledVersion
+ ServerVersion = $ServerVersion
+ DualPublish = $true
+}
+if ($CompatibilityUpdater) { $deployArgs.CompatibilityUpdater = $CompatibilityUpdater }
+
+& (Join-Path $root 'deploy-local.ps1') @deployArgs
+if ($LASTEXITCODE -ne 0) { throw "deploy-local.ps1 -DualPublish failed (exit $LASTEXITCODE)." }
+
+$nextServerDir = Join-Path $root '.local_deploy\server\v2'
+if (-not (Test-Path $nextServerDir)) {
+ throw "Expected /v2/ server dir at '$nextServerDir' but it does not exist."
+}
+$nextServerUri = "file:///$(((Resolve-Path $nextServerDir).Path -replace '\\','/'))"
+
+& (Join-Path $root 'ModdingToolBase\scripts\Test-LocalUpdateCycle.ps1') `
+ -AppExePath (Join-Path $root '.local_deploy\install\RaW-DevLauncher.exe') `
+ -ServerUri $nextServerUri `
+ -Branch $Branch `
+ -NoUpdateMessage 'No update available.' `
+ -ExpectedNewVersion $ServerVersion
+
+exit $LASTEXITCODE
\ No newline at end of file
diff --git a/test-local-update.ps1 b/test-local-update.ps1
new file mode 100644
index 0000000..5a5f69e
--- /dev/null
+++ b/test-local-update.ps1
@@ -0,0 +1,37 @@
+# =========================================================================================
+# Local bootstrap for the self-update integration test.
+#
+# Stages a local deploy via deploy-local.ps1, then runs the shared end-to-end test from
+# ModdingToolBase against the staged install dir + signed local server.
+#
+# Windows-only.
+# =========================================================================================
+
+#Requires -Version 7.0
+
+[CmdletBinding()]
+param(
+ [string]$InstalledVersion = '0.0.1-local',
+ [string]$ServerVersion = '99.99.99-local',
+ [string]$Branch = 'beta'
+)
+
+$ErrorActionPreference = 'Stop'
+
+$root = $PSScriptRoot
+if ([string]::IsNullOrEmpty($root)) { $root = Get-Location }
+
+& (Join-Path $root 'deploy-local.ps1') -InstalledVersion $InstalledVersion -ServerVersion $ServerVersion
+if ($LASTEXITCODE -ne 0) { throw "deploy-local.ps1 failed (exit $LASTEXITCODE)." }
+
+$serverDir = Join-Path $root '.local_deploy\server'
+$serverUri = "file:///$(((Resolve-Path $serverDir).Path -replace '\\','/'))"
+
+& (Join-Path $root 'ModdingToolBase\scripts\Test-LocalUpdateCycle.ps1') `
+ -AppExePath (Join-Path $root '.local_deploy\install\RaW-DevLauncher.exe') `
+ -ServerUri $serverUri `
+ -Branch $Branch `
+ -NoUpdateMessage 'No update available.' `
+ -ExpectedNewVersion $ServerVersion
+
+exit $LASTEXITCODE
\ No newline at end of file
diff --git a/tools/v1/AnakinRaW.ExternalUpdater.exe b/tools/v1/AnakinRaW.ExternalUpdater.exe
new file mode 100644
index 0000000..c03ac56
Binary files /dev/null and b/tools/v1/AnakinRaW.ExternalUpdater.exe differ
diff --git a/version.json b/version.json
index c3a55c9..0157ac1 100644
--- a/version.json
+++ b/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
- "version": "2.0",
+ "version": "3.0",
"publicReleaseRefSpec": [
"^refs/heads/main$"
],