-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auto-update functionality #50
base: main
Are you sure you want to change the base?
Conversation
Omitting LangVersion causes compile to use latest C# version corresponding that that .NET release. So essentially updating .NET version also updates language version. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version
Includes fix from Jun 22. Messy commit, see code review comment
This commit no longer needs some of these formatting since we'll run CSharpier soon, but I'd have to spend ages resolving merge conflicts to get rid of them, just ignore. (Tom 09/07)
Discussed with BIRD and he agrees, simplest if we explicitly use ASCII everywhere. I was a bit confused why we had some cases of Encoding.Default and others with ASCII, good to be explicit.
…me places I decided to merge two commits together as they had so much overlap. I'm getting the use of underscores for private members consistent, then doing some hard renames like Property -> EntityProperty (I really really hate use C# words like "Property" as classes)
NLog is more flexible then MS's Logger, can be configured via nlog.config, and much nicer to use without DI.
Sorry, hard to split this up well. Stripper: didn't touch much RunExternalTool: altered to always use a tmp file, and to log the output of the executable. ReplaceTexture: rewrote so string/regex replacing is combined. bit more code here, but the UI code is drastically better because of it. Compression: Deleted, it's just put of the save process now.
Being lazy and doing this all in one commit. Used Rider to delete all the unused, shouldn't need reviewing.
Can't take a ref to a property (cursed), so make the class implement a resize method
- Support for compression pakfile items - Switches from storing compression state/desired compression on lumps, save message gets passed it - Adds progress updating - Adds cancellation support - Improved debug ouput
Fixes momentum-mod#45. I wasn't updating compression state properly, screwed everything up. Gamelumps were a bastard.
b4c5c0c
to
dcac590
Compare
Oh and for commit messages please see https://github.com/momentum-mod#contribution-guidelines. |
Hey - thanks a ton for the code review, it means a lot! I've rebased and made the requested changes. There's significant code duplication in the method HttpDownload as I modified that from BspService.cs in order to grab the progress bar code - I think that could be replaced inside a more generic class but I don't want to step on toes while you're refactoring the UI. Let me know if this works. Thanks! |
src/Lumper.UI/Updater/Updater.cs
Outdated
throw new Exception("Could not parse Major/Minor/Patch version."); | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throwing always halts execution, it's impossible for return null
to ever be reached. That's why I prefer inverting the above conditional and doing if (!match.Success) throw new InvalidDataException(...)
, then you don't need to indent the code block doing the version parsing. Just a bit clearer to the reader, they don't need to read back upwards to the match.success conditional to check why we're throwing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're still not throwing with if (!match.Success) throw ...
, your current approach is a lot harder to read.
src/Lumper.UI/Updater/Updater.cs
Outdated
} | ||
else | ||
{ | ||
throw new Exception("Could not connect - error " + response.StatusCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best to avoid generic exceptions, there's probably some HttpException or similar provided by the HttpClient library. Usually easy to find in an editor with code completion, just type Http and let it autocomplete.
src/Lumper.UI/Updater/Updater.cs
Outdated
ButtonResult result = await MessageBoxManager | ||
.GetMessageBoxStandard( | ||
"Error", | ||
ex.Message, ButtonEnum.Ok) | ||
.ShowWindowDialogAsync(Program.Desktop.MainWindow); | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really don't need to be catching exceptions here. Just put a big try/catch in the Update()
function and let these bubble up.
src/Lumper.UI/Updater/Updater.cs
Outdated
ButtonResult result = await MessageBoxManager | ||
.GetMessageBoxStandard( | ||
"Error", | ||
"IO error: " + ex.Message, ButtonEnum.Ok) | ||
.ShowWindowDialogAsync(Program.Desktop.MainWindow); | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More message boxes that should be logger errors
src/Lumper.UI/Updater/Updater.cs
Outdated
try | ||
{ | ||
var fileURL = GetPath(assets, OSPlatform.Linux); | ||
var fileName = "linux_" + latest.ToString() + ".zip"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you do $"linux_{latest}.zip"
tostring should get called for you!
src/Lumper.UI/Updater/Updater.cs
Outdated
{ | ||
ButtonResult result = await MessageBoxManager | ||
.GetMessageBoxStandard( | ||
"Error", | ||
"IO error: " + ex.Message, ButtonEnum.Ok) | ||
.ShowWindowDialogAsync(Program.Desktop.MainWindow); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Message box grr
src/Lumper.UI/Updater/Updater.cs
Outdated
GroupCollection currentVersion = match.Groups; | ||
|
||
|
||
int.TryParse(currentVersion[1].ToString(), out var major); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TryParse
here returns a boolean whether the parse was successful, which you're not using. TryParse
has an evil sibling, Parse
, which will throw if it fails. But as I've rambled about below, throwing for a parsing error like that is completely fine. In fact it'd be very weird if the parse failed here, given our regex was successful! So all you need is
return new Version(int.Parse(currentVersion[1].Value, currentVersion[2].Value, currentVersion[3].Value))`
note I'm using .Value
on the GroupCollection instead of ToString. If you peek at the source, ToString
just returns Value
, but Value
is already a string and is what you should be accessing.
src/Lumper.UI/Updater/Updater.cs
Outdated
using Newtonsoft.Json; | ||
using NLog; | ||
|
||
internal sealed partial class Updater |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't really think internal
is necessary here.
Getting there! Sorry for chucking so much at you, hope the comments are informative. If you have questions about anything or what to discuss I'm happy to talk about this in our Discord in text chat or voice. I don't think the code duplication of HttpDownload is too bad, since I think your version should be a little different anyway, with the outer method controlling the IoHandler and IoProgressWindow. Oh, and if you could |
ButtonResult result = await MessageBoxManager | ||
.GetMessageBoxStandard( | ||
"No updates available", | ||
"No updates available", ButtonEnum.Ok) | ||
.ShowWindowDialogAsync(Program.Desktop.MainWindow); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to do a Logger.Info here I think. Maybe I'm weird but I don't like random message boxes popping up!
Lumper.UI.Updater.Updater.Version? updateAvailable = await Lumper.UI.Updater.Updater.CheckForUpdate(); | ||
if (updateAvailable != null) | ||
{ | ||
string updateNumber = updateAvailable.ToString(); | ||
ButtonResult result = await MessageBoxManager | ||
.GetMessageBoxStandard( | ||
"Update Available", | ||
$"An update is available ({updateNumber}), do you want to download and restart?", ButtonEnum.OkCancel) | ||
.ShowWindowDialogAsync(Program.Desktop.MainWindow); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually sorry, I suggested returning null here but I think a bool would be way easier to understand. So make method signature would be
bool CheckForUpdate(out Version version)
then in the method have something like
version = latest;
return (current != latest);
then here would be
var updateAvailable = await Lumper.UI.Updater.Updater.CheckForUpdate(out Version version);
if (updateAvailable)
{
ButtonResult result = await MessageBoxManager
.GetMessageBoxStandard(
"Update Available",
$"An update is available ({version), do you want to download and restart?", ButtonEnum.OkCancel)
.ShowWindowDialogAsync(Program.Desktop.MainWindow);
Also import is quite weird, just do import Lumper.UI.Updater
then you can do just e.g. Updater.CheckForUpdate()
src/Lumper.UI/Views/MainWindow.axaml
Outdated
<Separator /> | ||
<MenuItem Header="Check for updates" Command="{Binding UpdateCommand}" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's split this into an "About" section that just has this.
@BIRD311 do you want to take a look at this? Really hate to burden you with every more review work, but if you were up for testing on linux and maybe glancing at the code that'd be great. Up to you! |
eb0f36d
to
71e4b76
Compare
Hey - thanks a ton for the code review and thanks for your patience. I've made the changes and squashed them. Hoping I did that correctly! The one change I couldn't do was regarding CheckForUpdate - C# doesn't support async with 'out' modifiers, so I changed that to a Tuple that contains the success/fail and the Version. I'll see if I can test the Linux code later. |
On phone so hard to review but I noticed you're using Tuple when you can use the new tuple syntax of e.g. For Git stuff, probably the easiest approach is to do a mixed reset to unstage everything, then just recommit in two different commits. To clarify what we want the commit tree to look like, we want
Anything brought up in review should just be fixup/amended to that second commit (just stage those changes then I noticed dotnet format formatted a bunch of csproj files that it shouldn't, this is because we haven't finished the auto formatting system yet, please just discard any changes made to files you're not interacting with in this PR. For the assemblyversion thing, probably easiest to just discard all changes to that file then add the assemblyversion line back in your editor. Sorry this is so messy, will be sorted soon! To recommit I'd do
Check logs to make sure yt makes sense before pushing. This is rushed sorry, on phone in a coffee shop and only have a few minutes. If struggling with any git stuff I highly recommend a graphical client, my preferred client is Fork https://git-fork.com/ (free, just slams asking for money occasionally) |
Thanks for the patience and the explanation - I've fixed the commit history now and it should be clean of the formatting edits. |
Aaaalmost, just you've still got a fixup commit. If you just reorder the commit with the UI stuff and the commit with the assemblyversion stuff, you can easily amend any changes you have to make to the UI stuff using To order do Also, I'd prefer you keep the commit messages shorter, as you can see in the commit messages when shown on Github get trimmed awkwardly. Also "updater" isn't really a valid scope, I'd just use the messages in my above message. You can rename during the interactive rebase above. When you enter the rebase, you'll see an editor looking something like this
to reorder and reword, change these lines to
that'll reword the existing commits, switch their order, then fixup the commit with the "fixup!" on the front into the commit above it. Sorry if that's a bit overwhelming, but if you can make sense of that, you've just learnt probably the single most powerful feature in Git! |
a2c689b
to
1c02fff
Compare
Got it (I think!). Sorry, I'm not great with git but I appreciate the hand holding. |
Yeah looks good. And no problem, we're pretty exacting with structuring commits so naturally we're happy to help people struggling with it. Git is a hassle to learn at first, with a steep learning curve, but given how ubiquitous it's become, it's well worth putting the time into learning! I had a quick glance at your commits and mostly looking good though spotted a few small things, one that you're still using Really appreciate the work by the way, and sticking through everything brought up in review. We'll soon be encouraging everyone working on map porting to start using Lumper, which is a vital part of getting Momentum in a releasable state. Great to have other devs working on it! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay sorry, since this is taking a while and finding it harder to explain some of the larger issues, I'm going to just checkout this branch and clean it up a bit, rather than post code snippets. I'll make a separate commit for it, please take a look through the changes and make sure they make sense to you, then fixup my commit in yours when ready.
src/Lumper.UI/Updater/Updater.cs
Outdated
/// Checks for possible update on the Github releases page by the tag name | ||
/// </summary> | ||
/// <returns>A tuple where the first value defines if there is an update ready, and the second value is the latest Version</returns> | ||
public static async Task<Tuple<bool, Version>> CheckForUpdate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Task(bool, Version)
src/Lumper.UI/Updater/Updater.cs
Outdated
GithubUpdate assets; | ||
assets = await GetGithubUpdates(); | ||
|
||
//parse tag name to find the current and latest version | ||
//finding the format of xx.yy.zz | ||
Version current; | ||
Version latest; | ||
current = GetVersionFromString(Assembly.GetExecutingAssembly().GetName().Version.ToString()); | ||
latest = GetVersionFromString(assets.TagName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GithubUpdate assets = await GetGithubUpdates();
// Parse tag name to find the current and latest version
// Finding the format of xx.yy.zz
Version current = GetVersionFromString(Assembly.GetExecutingAssembly().GetName().Version.ToString());
Version latest = GetVersionFromString(assets.TagName);
return (current != latest, latest);
src/Lumper.UI/Updater/Updater.cs
Outdated
} | ||
public static async Task<Stream?> HttpDownload(string url, string fileName, IoProgressWindow progressWindow, IoHandler handler, CancellationTokenSource cts) | ||
{ | ||
//the amount the progress bar should go up to while downloading - we still have to unzip for the other percentage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please capitalise comments and put a space after //
. I know some comments in the pre-refactor/cleanup
looked like this but would rather be consistent from now on. It takes no effort for programmers to capitalise properly, even though it's being very pedantic, I don't see a good excuse for not capitalising!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Further comments below, please can you scan through and upset all of them. Sorry to be so annoying about this!
|
||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); | ||
|
||
private const float DownloadProgressPercentage = 80; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird to put this so far from the code actually using it. You can use const
for a variable declaration inside a method, doesn't have to be a field, just put it in the http downloading stuff!
src/Lumper.UI/Updater/Updater.cs
Outdated
} | ||
|
||
} | ||
throw new Exception("Could not find download link for " + nameof(OS)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nameof(OS)
is just "OS", you want + OS
);
src/Lumper.UI/Updater/Updater.cs
Outdated
List<Task> tasks = new List<Task>(); | ||
List<FileStream> fileStreams = new List<FileStream>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best to use var
on LHS, no need to specific a type of both sides of the assignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nvm these were totally unused!
/// Downloads an update for the program, applies it, and then restarts itself with the new version | ||
/// </summary> | ||
/// <returns></returns> | ||
public static async ValueTask Update() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole method can be reorganised to avoid all the dupe code. For sake of time I'm going to just write the whole thing without the branching, please use it!
src/Lumper.UI/Updater/Updater.cs
Outdated
{ | ||
assets = await GetGithubUpdates(); | ||
|
||
Version? latest; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be Version latest = GetVersionFromString(assets.TagName);
note that latest is non-null. Idk if your editor has LSP supported but this should be warning at you about this, don't ignore it!
src/Lumper.UI/Updater/Updater.cs
Outdated
var operatingSystem = ""; | ||
|
||
if (OS == OSPlatform.Windows) | ||
operatingSystem = "win"; | ||
else if (OS == OSPlatform.Linux) | ||
operatingSystem = "linux"; | ||
else | ||
throw new ApplicationException("Unsupported OS: " + OS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're picking this same string in Update
, so let's just pass that in to this function. I'll post a rewritten version of Update
below, you can modify this function to take a string input.
src/Lumper.UI/Updater/Updater.cs
Outdated
/// Parses a string to find the major/minor/patch versioning | ||
/// </summary> | ||
/// <param name="s">String containing a version number following the format 'xx.yy.zz'</param> | ||
private static Version GetVersionFromString(string s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better name is ParseVersion
Okay done, though haven't tested. Please take a look and make sure everything works and makes sense to you, then fixup into your commit! Spent a while trying to do nice JSON parsing but since we're using Newtonsoft stuff still, we can't use some of the nice new stuff outlined here. Oh well! |
Thanks for that - I've commited a couple of fixes:
To work properly JsonConvert.DeserializeObject needs the setter otherwise it won't update the fields.
The file for Windows is along the lines of "Lumper_v0.1.1_win-x64.zip" so searching if the string cotntains "Windows" fails. |
Oh gotcha, both those fixes make sense. We're probably about ready to merge but going to give BIRD the opportunity to look at this, and not sure if we'll merge this into By the way, I have a big load of Lumper issues I was meaning to make issues for and haven't had a chance to, if you're interested in helping out further keep an eye on the issues board! |
Just thinking about this a little today, might be a better approach to pull the updating logic out to a separate executable so we can copy and replace stuff easier. Though I guess we'd have to do some work on startup of the main app to determine if we've just updated, and if so, replace the old version of the updater executable with the new one. Not sure we need to do this, want to hear what others think in our team meeting tomorrow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only skimmed this, don't personally care much about the feature. Thank for helping.
var fileName = $"{osName}_{latest}.zip"; | ||
var directoryName = $"{fileName}temp"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
write the download to /tmp
so it will be cleaned up if something goes wrong and we don't make it to the rm
in code. There should be a c# function to get the temp folder or a temp file independent of the platform we're on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't sure how long this is guaranteed to last, turns out on Windows, the OS doesn't clean up the files for you! I was under the impression they were wiped on reboot, which AFAIK is what all unix-like systems with a /tmp
do. So, we should still rm
the file after.
os == OSPlatform.Linux | ||
? $""" | ||
sleep 2 && | ||
yes | cp -rf "{currentDirectory}\{directoryName}" && | ||
rm "{currentDirectory}\{fileName}" && | ||
rm -rf "{currentDirectory}\{directoryName}" && | ||
./Lumper.UI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- copy doesn't have a destination
rm -rf
is scary, try moving files instead- you can't rely on the current directory being where
./Lumper.UI
is - forward slashes in the path
Discussed this a bit yesterday and keen to avoid the shell scripts. Most of the code here we'll still use, but we'll probably pull some of it out to separate executable. Going to wait until |
This is regarding this issue: #42
The code in this PR first checks if the version number defined in does not match the most recent version on Github. If it doesn't, it downloads the zip, unpacks it and then copies it over the root directory. My linux partition is broken right now so only the Windows sections of the code have been tested.
Let me know if there's anything I can improve on in this PR!
Thanks!