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

feat: expose auto-updater method to update from file on disk #45406

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/browser/api/auto-updater/auto-updater-win.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
}
}

prepareUpdateFromFile () {
throw new Error('Not supported on Windows');
}

// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
emitError (error: Error) {
Expand Down
1 change: 1 addition & 0 deletions patches/squirrel.mac/.patches
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ fix_abort_installation_attempt_at_the_final_mile_if_the_app_is.patch
feat_add_ability_to_prevent_version_downgrades.patch
refactor_use_non-deprecated_nskeyedarchiver_apis.patch
chore_turn_off_launchapplicationaturl_deprecation_errors_in_squirrel.patch
hack_expose_verifyandprepareupdateexternal.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Julian Waller <[email protected]>
Date: Fri, 31 Jan 2025 01:10:05 +0000
Subject: hack: expose verifyAndPrepareUpdateExternal


diff --git a/Squirrel/SQRLUpdater.h b/Squirrel/SQRLUpdater.h
index 87119399e8548e04134d1ee0cd18f85c2ad672c2..3d0435b80b8f23f129edff9b188f32c8af3b48d5 100644
--- a/Squirrel/SQRLUpdater.h
+++ b/Squirrel/SQRLUpdater.h
@@ -9,6 +9,8 @@
#import <Foundation/Foundation.h>
#import <ReactiveObjC/ReactiveObjC.h>

+#import "SQRLUpdate.h"
+
// Represents the current state of the updater.
//
// SQRLUpdaterStateIdle - Doing absolutely diddly squat.
@@ -144,7 +146,7 @@ typedef enum {
//
// Returns the initialized `SQRLUpdater`.
- (id)initWithUpdateRequest:(NSURLRequest *)updateRequest
- forVersion:(NSString*)version;
+ forVersion:(NSString*)version;

// Initializes an updater that will send the given request to check for updates
// and passes a block to provide requests for the update downloads.
@@ -197,6 +199,15 @@ typedef enum {
// wrong before termination. The signal will never complete.
- (RACSignal *)relaunchToInstallUpdate;

+// Validates the code signature of the given update bundle, then prepares it for
+// installation.
+//
+// Upon success, the update will be automatically installed after the
+// application terminates.
+//
+// update - Describes the update to verify and prepare. This must not be nil.
+- (RACSignal *)verifyAndPrepareUpdateExternal:(SQRLUpdate *)update fromBundle:(NSBundle *)updateBundle;
+
- (BOOL)isRunningOnReadOnlyVolume;
- (RACSignal *)updateFromJSONData:(NSData *)data;

diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
index 592c7ea51515aab96934e0117df3c8065494fa09..9039276910b6a8b26aabfb385827f9430b3a6e9b 100644
--- a/Squirrel/SQRLUpdater.m
+++ b/Squirrel/SQRLUpdater.m
@@ -722,6 +722,48 @@ - (RACSignal *)truncateLogs {

#pragma mark Installing Updates

+- (RACSignal *)verifyAndPrepareUpdateExternal:(SQRLUpdate *)update fromBundle:(NSBundle *)updateBundle {
+ NSParameterAssert(update != nil);
+ NSParameterAssert(updateBundle != nil);
+
+ if (_signature == nil) {
+ NSError *error = nil;
+ _signature = [SQRLCodeSignature currentApplicationSignature:&error];
+ if (_signature == nil) {
+ #if DEBUG
+ NSLog(@"Could not get code signature for running application, application updates are disabled: %@", error);
+ return nil;
+ #else
+ NSDictionary *exceptionInfo = @{ NSUnderlyingErrorKey: error };
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Could not get code signature for running application" userInfo:exceptionInfo];
+ #endif
+ }
+ }
+
+ if (!_shipItLauncher) {
+ @weakify(self)
+ _shipItLauncher = [[[RACSignal
+ defer:^{
+ @strongify(self);
+
+ NSURL *targetURL = NSRunningApplication.currentApplication.bundleURL;
+
+ BOOL targetWritable = [self canWriteToURL:targetURL];
+ BOOL parentWritable = [self canWriteToURL:targetURL.URLByDeletingLastPathComponent];
+ BOOL launchPrivileged = !targetWritable || !parentWritable;
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"SquirrelMacEnableDirectContentsWrite"]) {
+ // If SquirrelMacEnableDirectContentsWrite is enabled we don't care if the parent directory is writeable or not
+ BOOL launchPrivileged = !targetWritable;
+ }
+ return [SQRLShipItLauncher launchPrivileged:launchPrivileged];
+ }]
+ replayLazily]
+ setNameWithFormat:@"shipItLauncher"];
+ }
+
+ return [self verifyAndPrepareUpdate:update fromBundle:updateBundle];
+}
+
- (RACSignal *)verifyAndPrepareUpdate:(SQRLUpdate *)update fromBundle:(NSBundle *)updateBundle {
NSParameterAssert(update != nil);
NSParameterAssert(updateBundle != nil);
2 changes: 2 additions & 0 deletions shell/browser/api/electron_api_auto_updater.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ gin::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
.SetMethod("isVersionAllowedForUpdate",
&auto_updater::AutoUpdater::IsVersionAllowedForUpdate)
#endif
.SetMethod("prepareUpdateFromFile",
&auto_updater::AutoUpdater::PrepareUpdateFromFile)
.SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall);
}

Expand Down
4 changes: 4 additions & 0 deletions shell/browser/auto_updater.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ void AutoUpdater::CheckForUpdates() {}

void AutoUpdater::QuitAndInstall() {}

bool AutoUpdater::PrepareUpdateFromFile(gin::Arguments* args) {
return false;
}

bool AutoUpdater::IsVersionAllowedForUpdate(const std::string& current_version,
const std::string& target_version) {
return false;
Expand Down
1 change: 1 addition & 0 deletions shell/browser/auto_updater.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class AutoUpdater {
static void SetFeedURL(gin::Arguments* args);
static void CheckForUpdates();
static void QuitAndInstall();
static bool PrepareUpdateFromFile(gin::Arguments* args);

static bool IsVersionAllowedForUpdate(const std::string& current_version,
const std::string& target_version);
Expand Down
62 changes: 62 additions & 0 deletions shell/browser/auto_updater_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,68 @@
}
}

bool AutoUpdater::PrepareUpdateFromFile(gin::Arguments* args) {
gin_helper::ErrorThrower thrower(args->isolate());

if (update_url_ != "") {
thrower.ThrowError(
"prepareUpdateFromFile cannot be called after setFeedUrl");
return false;
}

if (g_update_available) {
thrower.ThrowError("An update is already pending");
return false;
}

std::string filename;
v8::Local<v8::Value> first_arg = args->PeekNext();
if (!first_arg.IsEmpty() && first_arg->IsString()) {
args->GetNext(&filename);
} else {
thrower.ThrowError("missing filename of bundle");
return false;
}

Delegate* delegate = GetDelegate();
if (!delegate) {
thrower.ThrowError("updater not fully initialised!");
return false;
}

if (g_updater)
g_updater = nil;

// Initialize the SQRLUpdater.
g_updater = [SQRLUpdater alloc];

// TODO - convert better
NSURL* url = [NSURL URLWithString:base::SysUTF8ToNSString(filename)];
NSBundle* bundle = [NSBundle bundleWithURL:url];

if (!bundle) {
thrower.ThrowError("app bundle not found!");
return false;
}

SQRLUpdate* update =
[SQRLUpdate alloc]; // This appears to not really be used, but is a
// necessary parameter

[[g_updater verifyAndPrepareUpdateExternal:update fromBundle:bundle]
subscribeError:^(NSError* error) {
if (delegate)
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription),
error.code, base::SysNSStringToUTF8(error.domain));
}
completed:^() {
g_update_available = true;
delegate->OnUpdateAvailable();
}];

return true;
}

bool AutoUpdater::IsVersionAllowedForUpdate(const std::string& current_version,
const std::string& target_version) {
return [SQRLUpdater
Expand Down