Skip to content
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

Adding update command #418

Merged
merged 27 commits into from
Dec 21, 2023
Merged

Adding update command #418

merged 27 commits into from
Dec 21, 2023

Conversation

gord5500
Copy link
Contributor

@gord5500 gord5500 commented Dec 7, 2023

Why

Need a command to update bmx with a new version if one is available

@github-actions github-actions bot added language/csharp size/L A large PR. Could this be broken up? labels Dec 7, 2023
Copy link
Contributor

github-actions bot commented Dec 7, 2023

PR #419 created to format .cs files.

@github-actions github-actions bot added size/M A medium-sized PR. and removed size/L A large PR. Could this be broken up? labels Dec 12, 2023
Copy link
Contributor

PR #420 created to format .cs files.

Copy link
Contributor

PR #421 created to format .cs files.

Copy link
Contributor

PR #422 created to format .cs files.

Copy link
Contributor

PR #423 created to format .cs files.

Copy link
Contributor

PR #424 created to format .cs files.

Copy link
Contributor

PR #425 created to format .cs files.

@github-actions github-actions bot added the size/L A large PR. Could this be broken up? label Dec 19, 2023
Copy link
Contributor

PR #427 created to format .cs files.

Copy link
Contributor

PR #428 created to format .cs files.

@alex-fang0 alex-fang0 self-assigned this Dec 19, 2023
@@ -11,4 +11,5 @@ internal static class BmxPaths {
public static readonly string SESSIONS_FILE_LEGACY_NAME = Path.Join( BMX_DIR, "sessions" );
public static readonly string UPDATE_CHECK_FILE_NAME = Path.Join( CACHE_DIR, "updateCheck" );
public static readonly string AWS_CREDS_CACHE_FILE_NAME = Path.Join( CACHE_DIR, "awsCreds" );
public static readonly string OLD_BMX_VERSION_FILE_NAME = Path.Join( BMX_DIR, "bmx" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried running the update, and got this:

image

thoughts:

  • would prefer that we hide the backup files in a subdirectory ("temp"? "old-exe"? or a better name). We made an effort to clean up the top level .bmx directory to only user facing things. Let's keep it clean. It should make cleanup safer too.
  • The filename could be better formatted:
    • Add a separator between "bmx" and the version string
    • Use a different separator that's not dot to avoid confusion with the dots in the version string, e.g. bmx-v1.2.3-old.bak
    • Consider adding a timestamp or a random string to the name. This may not be necessary, but might be nice if a user updated multiple BMX binaries of the same version on the machine.
  • The variable OLD_BMX_VERSION_FILE_NAME is misnamed. It's not really used as a filename, but a prefix. However, if we put old binaries inside a subfolder, we only need to track the subfolder name, and not the filename prefix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that makes sense! Added a temp folder for the old versions and millisecond timestamp for the file.

}

internal static class GithubUtilities {
public static string GetReleaseVersion( GithubRelease? releaseData ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer if we don't allow calling such a method on nulls at all. I'd also argue that we should return null (instead of empty string) on a non-null GithubRelease too if it doesn't have a version.
I explained the reasoning here: #259 (comment)

Actually... it looks like we always convert the string to a Version object after calling this method, so might as well return a Version here directly? Strongly typed API is always nice. (this suggestion is entirely optional)

Also, as Adipa said, moving this method into the GithubRelease record itself would be even better, and takes care of part of the null problem too.


The end result could look something like this for the caller:

var releaseData = await GetLatestReleaseDataAsync();
var version = releaseData?.GetVersion();
if( version is null ) {
  // handle error - throw or early return
}
// happy path

Currently, we're using empty string to avoid handling the error case, even though our data model totally permits a non-existent version. If GitHub does return a non-existent version somehow, BMX would throw an unexpected exception with non-descript error message.
Not falling back to empty string forces us to be consistent in our data model & behaviour, and handle all expected errors.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense that we have consistency and clarity on returning nulls. Made some changes to put the methods into the record itself

src/D2L.Bmx/GithubRelease.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateChecker.cs Show resolved Hide resolved
internal class UpdateHandler {

public async Task HandleAsync() {
using var httpClient = new HttpClient();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably have enough GitHub interaction code to warrant a GitHubClient class. Then, ideally, I'd inject that into dependent classes via constructor parameters, and avoid instantiating new clients directly.
DI is generally nice (for testing and dependency/behaviour tracking), and consistent with our general coding practice.
I'm willing to let this PR through without DI for now, but I would like us to refactor this soon-ish.

src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
Copy link
Contributor

PR #429 created to format .cs files.

Copy link
Contributor

PR #430 created to format .cs files.

src/D2L.Bmx/GithubRelease.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
Comment on lines 53 to 54
} catch( Exception ex ) {
File.Move( backupPath, currentFilePath );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of moving the current file to backup path first then moving it back on error, how about downloading and extracting the new file into temp directory first, then do all the moves afterwards?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for the TarFile extract we don't set or return the file name so I think it might be a bit complicated keeping track of which files to move in the directory if there are multiple updates running.

Copy link
Member

@cfbao cfbao Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, when I said "temp directory" I was actually thinking of a uniquely named subdirectory in the system temp directory, i.e. what's returned by Path.GetTempFileName.

Even if we use ~/.bmx/temp, we can also create more subdirectories in it with unique names, e.g.

~/.bmx/temp/58ba67a7-10f3-418c-a686-90894c2b6045/
~/.bmx/temp/b90477fc-469b-4651-8ace-0ab522c935c8/
...

we can generate and keep track of the UUID (or other types of unique identifiers e.g. timestamps?) in memory, and not worry about multiple runs interfering with each other.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a commit trying out the temp directory first to push moving files to the end, seems a bit messy so I'm not sure if we should go for this. We can revert if needed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new change looks good. We can make it cleaner, but even as is it doesn't look too messy to me.
I do vastly prefer the new approach of "not moving files until we have everything ready".

} else if( extension.Equals( ".gz", StringComparison.OrdinalIgnoreCase ) ) {
ExtractTarGzipFile( downloadPath, currentDirectory );
} else {
throw new Exception( "Unknown archive type" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use BmxException with better error message

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already a nested exception (not sure if we can un-nest it without making the block messy) so I don't think the user will see it. I think the "Failed to update with new files" message should capture this tho

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see. Didn't notice the nesting. I'd still like to avoid it, but it's good for now.

src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
src/D2L.Bmx/UpdateHandler.cs Outdated Show resolved Hide resolved
Copy link
Contributor

PR #431 created to format .cs files.

Copy link
Contributor

PR #432 created to format .cs files.

Copy link
Contributor

PR #433 created to format .cs files.

Auto format liam_update_test

Co-authored-by: DotNet Format Bot <[email protected]>
cfbao
cfbao previously approved these changes Dec 21, 2023
throw new Exception( "Unknown archive type" );
}
} catch( Exception ex ) {
Directory.Delete( extractFolder, true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boolean literals should almost always be named explicitly

Suggested change
Directory.Delete( extractFolder, true );
Directory.Delete( extractFolder, recursive: true );

similarly for other Directory.Delete calls

Comment on lines 150 to 151
string destinationPath = Path.GetFullPath( Path.Combine( decompressedFilePath!, entry.FullName ) );
if( destinationPath.StartsWith( decompressedFilePath!, StringComparison.Ordinal ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decompressedFilePath isn't nullable

Suggested change
string destinationPath = Path.GetFullPath( Path.Combine( decompressedFilePath!, entry.FullName ) );
if( destinationPath.StartsWith( decompressedFilePath!, StringComparison.Ordinal ) ) {
string destinationPath = Path.GetFullPath( Path.Combine( decompressedFilePath, entry.FullName ) );
if( destinationPath.StartsWith( decompressedFilePath, StringComparison.Ordinal ) ) {

@alex-fang0 alex-fang0 merged commit 38aed87 into main Dec 21, 2023
9 checks passed
@alex-fang0 alex-fang0 deleted the liam_update_test branch December 21, 2023 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
language/csharp size/L A large PR. Could this be broken up?
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants