Skip to content

Commit 882c46d

Browse files
committed
fix: keep settings across reinstalls and put a setup on its build's channel
A fresh nightly install stayed on the stable update channel, the default of the settings, and so never updated again until a stable release passed its version. A reinstall did not help either: Velopack's setup replaces the whole install folder, and the cfg folder with the settings lived in it. The nightly workflow now passes -p:UpdateChannel=Nightly, which the app carries as a compile constant. The first launch after a setup, which Velopack signals through its first run hook, puts the update channel on the channel of the installed build. Updates do not run this, so a channel picked in the settings survives them. Installed builds keep cfg (settings, logs, detections, the webview profile) in the roaming AppData folder instead of the install folder and move an existing cfg there once. Development builds keep using the cfg folder next to their output. libobs is also pointed at its data and module folders by full path. The relative paths only worked when the working directory was the app folder, which is the case for the shortcuts but not for any other way of starting it.
1 parent 67380ae commit 882c46d

6 files changed

Lines changed: 84 additions & 7 deletions

File tree

.github/workflows/dotnet.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ jobs:
117117
- name: Build RePlays
118118
env:
119119
CI: false
120-
run: dotnet publish /p:Configuration=Release /p:Version=${{needs.prep.outputs.version}} /p:PublishProfile=FolderProfile -v d
120+
run: dotnet publish /p:Configuration=Release /p:Version=${{needs.prep.outputs.version}} /p:UpdateChannel=Nightly /p:PublishProfile=FolderProfile -v d
121121

122122
- name: Append version to setup file
123123
run: ren ./bin/Deployment/Releases/RePlays-win-Setup.exe RePlaysSetup-${{needs.prep.outputs.version}}.exe

Classes/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ static void Main(string[] args) {
7373
// there the migration done by "Update.exe start" (see Updater.Restart) takes care of it
7474
VelopackApp.Build()
7575
.SetLogger(new VelopackLogger())
76+
.OnFirstRun(_ => Updater.firstRunAfterSetup = true)
7677
.SetAutoApplyOnStartup(!Updater.IsSquirrelLayout())
7778
.Run();
7879

@@ -114,6 +115,7 @@ static void Main(string[] args) {
114115
}
115116
#endif
116117
SettingsService.LoadSettings();
118+
Updater.ApplyBuildChannel();
117119
SettingsService.UpdateGpuManufacturer();
118120
SettingsService.SaveSettings();
119121
StorageService.ManageStorage();

Classes/Recorders/LibObsRecorder.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,11 @@ public override void Start() {
201201
if (!obs_startup("en-US", null, IntPtr.Zero)) {
202202
throw new Exception("error on libobs startup");
203203
}
204-
obs_add_data_path("./data/libobs/");
205-
obs_add_module_path("./obs-plugins/64bit/", "./data/obs-plugins/%module%/");
204+
// full paths: libobs resolves relative ones against the working directory, which is
205+
// only the app folder when RePlays is started through one of its shortcuts
206+
var appDir = AppContext.BaseDirectory.Replace(Path.DirectorySeparatorChar, '/').TrimEnd('/');
207+
obs_add_data_path(appDir + "/data/libobs/");
208+
obs_add_module_path(appDir + "/obs-plugins/64bit/", appDir + "/data/obs-plugins/%module%/");
206209

207210
ResetAudio();
208211
ResetVideo();

Classes/Utils/Helpers.cs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,53 @@ public static string GetTempFolder() {
114114
return tempSaveDir;
115115
}
116116

117+
static string cfgFolder;
118+
static readonly object cfgFolderLock = new();
119+
120+
// settings, logs, detections and the webview profile. they used to live in the install
121+
// folder (cfg next to the current version), which velopack's setup wipes when it installs
122+
// over an existing install. an installed RePlays keeps them in the roaming AppData folder
123+
// (AppData/Roaming/RePlays) instead and moves what it finds in the old place there once.
124+
// a development build is not installed and keeps using the cfg folder next to its output
117125
public static string GetCfgFolder() {
118-
var cfgDir = Path.Join(GetStartupPath(), @"../cfg/");
119-
if (!Directory.Exists(cfgDir))
120-
Directory.CreateDirectory(cfgDir);
121-
return cfgDir;
126+
lock (cfgFolderLock) {
127+
if (cfgFolder != null) return cfgFolder;
128+
var legacyDir = Path.GetFullPath(Path.Join(GetStartupPath(), @"../cfg/"));
129+
var installed = File.Exists(Path.Join(GetStartupPath(), @"../Update.exe"));
130+
if (!installed) {
131+
Directory.CreateDirectory(legacyDir);
132+
return cfgFolder = legacyDir;
133+
}
134+
var dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"RePlays/");
135+
string moved = null;
136+
if (!File.Exists(Path.Join(dir, "userSettings.json")) && File.Exists(Path.Join(legacyDir, "userSettings.json"))) {
137+
try {
138+
CopyDirectory(legacyDir, dir);
139+
moved = $"Moved the cfg folder from {legacyDir} to {dir}";
140+
try {
141+
Directory.Delete(legacyDir, true);
142+
}
143+
catch (Exception exception) {
144+
moved += $" (the old folder could not be removed: {exception.Message})";
145+
}
146+
}
147+
catch (Exception exception) {
148+
moved = $"Could not move the cfg folder from {legacyDir} to {dir}: {exception.Message}";
149+
}
150+
}
151+
Directory.CreateDirectory(dir);
152+
cfgFolder = dir;
153+
if (moved != null) Logger.WriteLine(moved);
154+
return cfgFolder;
155+
}
156+
}
157+
158+
static void CopyDirectory(string source, string destination) {
159+
Directory.CreateDirectory(destination);
160+
foreach (var file in Directory.EnumerateFiles(source))
161+
File.Copy(file, Path.Join(destination, Path.GetFileName(file)), true);
162+
foreach (var directory in Directory.EnumerateDirectories(source))
163+
CopyDirectory(directory, Path.Join(destination, Path.GetFileName(directory)));
122164
}
123165

124166
public static string GetResourcesFolder() {

Classes/Utils/Updater.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ internal sealed class Updater {
1414
public static string currentVersion = "?";
1515
public static string latestVersion = "Offline";
1616
public static bool applyingUpdate { get; internal set; }
17+
// the release channel this build was made for (UpdateChannel property in RePlays.csproj)
18+
#if NIGHTLY
19+
public const string BuildChannel = "Nightly";
20+
#else
21+
public const string BuildChannel = "Stable";
22+
#endif
23+
// set by the velopack first run hook: this launch is the first after a setup ran
24+
public static bool firstRunAfterSetup;
1725
// the manager of the most recent update check, kept so a restart can install the update it downloaded
1826
static UpdateManager manager;
1927

@@ -40,6 +48,20 @@ public static bool IsSquirrelLayout() {
4048
return folder.StartsWith("app-", StringComparison.OrdinalIgnoreCase);
4149
}
4250

51+
// running a setup is a deliberate choice of channel: a nightly setup installs a version
52+
// that no stable release reaches for a long time, so left on the stable channel (the
53+
// default of a fresh install, or whatever a reinstall finds in the settings) it would
54+
// never update again. so the first launch after a setup puts the update channel on the
55+
// channel of the installed build. updates do not run this, a channel picked in the
56+
// settings survives them
57+
public static void ApplyBuildChannel() {
58+
if (!firstRunAfterSetup) return;
59+
var settings = SettingsService.Settings.generalSettings;
60+
if (settings.updateChannel == BuildChannel) return;
61+
Logger.WriteLine($"First run after the setup of a {BuildChannel} build, switching the update channel from {settings.updateChannel} to {BuildChannel}");
62+
settings.updateChannel = BuildChannel;
63+
}
64+
4365
public static async void CheckForUpdates(bool forceUpdate = false) {
4466
if (applyingUpdate) {
4567
Logger.WriteLine($"Currently in the middle of applying an update. Cannot check for updates.");

RePlays.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@
6262
<DefineConstants>TRACE;DEBUG</DefineConstants>
6363
</PropertyGroup>
6464

65+
<!-- the release channel this build is made for, Stable or Nightly. the nightly workflow
66+
passes -p:UpdateChannel=Nightly. the first launch after a setup puts the app's update
67+
channel on it (see Updater.ApplyBuildChannel) -->
68+
<PropertyGroup>
69+
<UpdateChannel Condition=" '$(UpdateChannel)' == '' ">Stable</UpdateChannel>
70+
<DefineConstants Condition=" '$(UpdateChannel)' == 'Nightly' ">$(DefineConstants);NIGHTLY</DefineConstants>
71+
</PropertyGroup>
72+
6573
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
6674
<DebugSymbols>false</DebugSymbols>
6775
</PropertyGroup>

0 commit comments

Comments
 (0)