Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Updater verification #8787

Merged
merged 16 commits into from
Jul 10, 2018
Merged

Updater verification #8787

merged 16 commits into from
Jul 10, 2018

Conversation

niklasad1
Copy link
Collaborator

@niklasad1 niklasad1 commented Jun 4, 2018

Attempt to close #8178

Abstract

In the entry point in parity/main we first try run the latest installed version of parity if not --force-restart is explicitly specified. If we try to run the latest version of parity(run_parity) it will return Some(ErrorCode) unless the downloaded binary is not found. Thus, if unsupported CLI args are entered for example it will terminate (return the exit code) instead of falling back to local version.
In other words, only if the binary is not found we fall back to the local version!

So, high-level this PR changes so run_parity() return Result<(), ErrorCode> and if an error received while running parity we fall back to the local version! That's it still, I think it's okay still store the newer version try it if, for example, other CLI args may be valid. Otherwise, it is easy to add a flag for that or change the timestamp of binary to disable it the entering the updater loop. But, the downloaded binary should be probably not be removed otherwise the updater will fetch the same binary over and over again!

Manual tests

$ cargo build --features=test-updater

Incompatible CLI args

➜  updater ./parity --auto-update=all --auto-update-delay=0 -l updater=trace
2018-06-04 21:03:24  main INFO parity::run  Starting Parity/v1.12.0-unstable-e2a90ce15-20180604/x86_64-linux-gnu/rustc1.26.1
2018-06-04 21:03:24  main INFO parity::run  Keys path /home/niklasad1/.local/share/io.parity.ethereum/keys/Foundation
2018-06-04 21:03:24  main INFO parity::run  DB path /home/niklasad1/.local/share/io.parity.ethereum/chains/ethereum/db/906a34e69aec8c0d
2018-06-04 21:03:24  main INFO parity::run  Path to dapps /home/niklasad1/.local/share/io.parity.ethereum/dapps
2018-06-04 21:03:24  main INFO parity::run  State DB configuration: fast
2018-06-04 21:03:24  main INFO parity::run  Operating mode: active
2018-06-04 21:03:24  main INFO ethcore_service::service  Configured for Foundation using Ethash engine
2018-06-04 21:03:25  main INFO parity_updater::updater  this is hardcoded to an old version this must be fixed before merging updater/src/updater.rs:393
2018-06-04 21:03:25  main TRACE updater  Current release is 1.3.7-stable-0x0000…0000 (0x0000000000000000000000000000000000000000)
2018-06-04 21:03:25  main INFO parity_updater::updater  The `security_level` check is DISABLED!!!!! It is just diabled to test the updater-verification and should be removed later, updater/src/updater.rs:614
2018-06-04 21:03:25  main INFO parity_updater::updater  The `updater_policy frequency` check is DISABLED!!!!!. This check is just diabled to test the updater and should be removed later, updater/src/updater.rs:621
2018-06-04 21:03:25  main TRACE updater  Looking up this_fork for our release: parity/0x0000000000000000000000000000000000000000
2018-06-04 21:03:25  main TRACE updater  Latest release in our track is v1.9.5-stable-0xff82…8db2 it is non-critical (x86_64-unknown-linux-gnu binary is 0xf447…3769)
2018-06-04 21:03:25  main TRACE updater  Fork: this/current/latest/latest-known: unreleased/#5732312/#4370000/#2675000
2018-06-04 21:03:25  main INFO updater  Update for binary 0xf447…3769 triggered
2018-06-04 21:03:25  main INFO updater  Attempting to get parity binary 0xf447…3769
2018-06-04 21:03:25  IO Worker #2 INFO network  Public node URL: enode://682b250793ef93e61a9f19918dd63e56695e24ef329a66d017a006b9145fc8b015a4d4a77954e1ef59febde23e17c26b12e087ae59cf03a8367de565208a8fdc@192.168.178.66:30303
2018-06-04 21:03:25  fetch WARN rustls::session  Sending warning alert CloseNotify
2018-06-04 21:03:25   INFO miner  Updated conversion rate to Ξ1 = US$589.22 (8081709 wei/gas)
2018-06-04 21:03:26  IO Worker #0 WARN discovery  Received ping from self
2018-06-04 21:03:26  IO Worker #0 WARN discovery  Received ping from self
2018-06-04 21:03:32   INFO updater  Fetched latest version (1.9.5-stable-0xff82…8db2) OK to /tmp/ECzpH9bP3JyQ
2018-06-04 21:03:33   INFO updater  Copied updated binary to /home/niklasad1/.local/share/io.parity.ethereum-updates/parity-1.9.5-ff821daf1da42865f229aee35f2e74e7b2dd8db2
2018-06-04 21:03:33   INFO updater  Completed upgrade to 1.9.5-stable-0xff82…8db2
2018-06-04 21:03:33  main INFO parity::run  Finishing work, please wait...
2018-06-04 21:03:36  main TRACE updater  Re-running updater loop
2018-06-04 21:03:36  main TRACE updater  latest binary path: File { fd: 3, path: "/home/niklasad1/.local/share/io.parity.ethereum-updates/latest", read: true, write: false }
2018-06-04 21:03:36  main TRACE updater  latest binary path: File { fd: 3, path: "/home/niklasad1/.local/share/io.parity.ethereum-updates/latest", read: true, write: false }
error: Found argument '--auto-update-delay' which wasn't expected, or isn't valid in this context
        Did you mean --auto-update?

USAGE:
    parity-1.9.5-ff821daf1da42865f229aee35f2e74e7b2dd8db2 [SUBCOMMAND]

For more information try --help
2018-06-04 21:03:36  main ERROR updater  Updated binary could not be executed error: StatusCode(1). Falling back to local version
2018-06-04 21:03:36  main INFO parity::run  Starting Parity/v1.12.0-unstable-e2a90ce15-20180604/x86_64-linux-gnu/rustc1.26.1
2018-06-04 21:03:36  main INFO parity::run  Keys path /home/niklasad1/.local/share/io.parity.ethereum/keys/Foundation
2018-06-04 21:03:36  main INFO parity::run  DB path /home/niklasad1/.local/share/io.parity.ethereum/chains/ethereum/db/906a34e69aec8c0d
2018-06-04 21:03:36  main INFO parity::run  Path to dapps /home/niklasad1/.local/share/io.parity.ethereum/dapps
2018-06-04 21:03:36  main INFO parity::run  State DB configuration: fast
2018-06-04 21:03:36  main INFO parity::run  Operating mode: active
2018-06-04 21:03:36  main INFO ethcore_service::service  Configured for Foundation using Ethash engine
2018-06-04 21:03:37  main INFO parity_updater::updater  this is hardcoded to an old version this must be fixed before merging updater/src/updater.rs:393
2018-06-04 21:03:37  main TRACE updater  Current release is 1.3.7-stable-0x0000…0000 (0x0000000000000000000000000000000000000000)
2018-06-04 21:03:37  main INFO parity_updater::updater  The `security_level` check is DISABLED!!!!! It is just diabled to test the updater-verification and should be removed later, updater/src/updater.rs:614
2018-06-04 21:03:37  main INFO parity_updater::updater  The `updater_policy frequency` check is DISABLED!!!!!. This check is just diabled to test the updater and should be removed later, updater/src/updater.rs:621
2018-06-04 21:03:37  main TRACE updater  Looking up this_fork for our release: parity/0x0000000000000000000000000000000000000000
2018-06-04 21:03:37  main TRACE updater  Latest release in our track is v1.9.5-stable-0xff82…8db2 it is non-critical (x86_64-unknown-linux-gnu binary is 0xf447…3769)
2018-06-04 21:03:37  main TRACE updater  Fork: this/current/latest/latest-known: unreleased/#5732321/#4370000/#2675000
2018-06-04 21:03:37  main INFO updater  Already fetched binary.
2018-06-04 21:03:37  main INFO updater  Completed upgrade to 1.9.5-stable-0xff82…8db2
2018-06-04 21:03:37  main INFO updater  Update installed, ready for restart.
2018-06-04 21:03:37  IO Worker #1 INFO network  Public node URL: enode://682b250793ef93e61a9f19918dd63e56695e24ef329a66d017a006b9145fc8b015a4d4a77954e1ef59febde23e17c26b12e087ae59cf03a8367de565208a8fdc@192.168.178.66:30303
2018-06-04 21:03:38  fetch WARN rustls::session  Sending warning alert CloseNotify
2018-06-04 21:03:38   INFO miner  Updated conversion rate to Ξ1 = US$589.22 (8081709 wei/gas)
^C2018-06-04 21:03:47  IO Worker #3 INFO import  Syncing #5732331 0x5a89…8961   1.00 blk/s  101.9 tx/s    8 Mgas/s      0+    7 Qed  #5732337    3/25 peers   549 KiB chain 98 MiB db 758 KiB queue 23 KiB sync  RPC:  0 conn,    0 req/s,    0 µs
2018-06-04 21:03:50  IO Worker #2 TRACE updater  Current release is 1.3.7-stable-0x0000…0000 (0x0000000000000000000000000000000000000000)
2018-06-04 21:03:50  IO Worker #2 INFO parity_updater::updater  The `security_level` check is DISABLED!!!!! It is just diabled to test the updater-verification and should be removed later, updater/src/updater.rs:614
2018-06-04 21:03:50  IO Worker #2 INFO parity_updater::updater  The `updater_policy frequency` check is DISABLED!!!!!. This check is just diabled to test the updater and should be removed later, updater/src/updater.rs:621
2018-06-04 21:03:50  IO Worker #2 TRACE updater  Looking up this_fork for our release: parity/0x0000000000000000000000000000000000000000
2018-06-04 21:03:50  IO Worker #2 INFO import  Imported #5732341 0xc6eb…fc62 (178 txs, 7.96 Mgas, 756 ms, 28.13 KiB) + another 2 block(s) containing 263 tx(s)

Compatible CLI args

➜  updater ./parity --auto-update=all -l updater=trace
2018-06-04 21:06:02  main INFO parity::run  Starting Parity/v1.9.5-stable-ff821da-20180321/x86_64-linux-gnu/rustc1.24.1
2018-06-04 21:06:02  main INFO parity::run  Keys path /home/niklasad1/.local/share/io.parity.ethereum/keys/Foundation
2018-06-04 21:06:02  main INFO parity::run  DB path /home/niklasad1/.local/share/io.parity.ethereum/chains/ethereum/db/906a34e69aec8c0d
2018-06-04 21:06:02  main INFO parity::run  Path to dapps /home/niklasad1/.local/share/io.parity.ethereum/dapps
2018-06-04 21:06:02  main INFO parity::run  State DB configuration: fast
2018-06-04 21:06:02  main INFO parity::run  Operating mode: active
2018-06-04 21:06:02  main INFO ethcore::service  Configured for Foundation using Ethash engine
2018-06-04 21:06:03  main WARN ethcore_network::node_table  Error reading node table file: ErrorImpl { code: Message("missing field `attempts`"), line: 8, column: 5 }
2018-06-04 21:06:03  main TRACE updater  Current release is 1.9.5-stable-ff82…8db2 (ff821daf1da42865f229aee35f2e74e7b2dd8db2)
2018-06-04 21:06:03   WARN jsonrpc_ipc_server::server  Removed existing file '/home/niklasad1/.local/share/io.parity.ethereum/jsonrpc.ipc'.
2018-06-04 21:06:03  IO Worker #3 INFO network  Public node URL: enode://682b250793ef93e61a9f19918dd63e56695e24ef329a66d017a006b9145fc8b015a4d4a77954e1ef59febde23e17c26b12e087ae59cf03a8367de565208a8fdc@192.168.178.66:30303
2018-06-04 21:06:03   INFO miner  Updated conversion rate to Ξ1 = US$589.22 (202042740 wei/gas)
2018-06-04 21:06:14  IO Worker #2 TRACE updater  Current release is 1.9.5-stable-ff82…8db2 (ff821daf1da42865f229aee35f2e74e7b2dd8db2)
2018-06-04 21:06:14  IO Worker #2 INFO import  Imported #5732348 c4ba…e5dd (130 txs, 7.99 Mgas, 456.96 ms, 27.09 KiB) + another 1 block(s) containing 91 tx(s)
2018-06-04 21:06:28  Verifier #7 TRACE updater  Current release is 1.9.5-stable-ff82…8db2 (ff821daf1da42865f229aee35f2e74e7b2dd8db2)

/cc @tomusdrw @andresilva

@5chdn 5chdn added A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. M4-core ⛓ Core client code / Rust. labels Jun 5, 2018
@5chdn 5chdn added this to the 1.12 milestone Jun 5, 2018
@niklasad1 niklasad1 changed the title [WIP] Updater verification Updater verification Jun 6, 2018
@niklasad1 niklasad1 added A0-pleasereview 🤓 Pull request needs code review. and removed A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. labels Jun 6, 2018
Copy link
Collaborator

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

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

Maybe I'm being paranoid here. Overall it seems like an improvement over current, but I think it'd be good to be extra careful when auto-updating executables.

@@ -375,12 +377,21 @@ impl Updater {
operations_contract::Operations::default(),
client.clone()),
exit_handler: Mutex::new(None),
this: VersionInfo::this(),
// this: VersionInfo::this(),
// TODO: Remove hardcoded dummy version for this
Copy link
Collaborator

Choose a reason for hiding this comment

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

Left-over TODO.

@@ -600,13 +611,15 @@ impl<O: OperationsClient, F: HashFetch, T: TimeProvider, R: GenRange> Updater<O,

// We rely on a secure state. Bail if we're unsure about it.
if self.client.upgrade().map_or(true, |c| !c.chain_info().security_level().is_full()) {
return;
info!("The `security_level` check is DISABLED!!!!! It is just diabled to test the updater-verification and should be removed later, {}:{}", file!(), line!());
// return;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Cleanup/TODO?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yepp, only for manual testing

}

// Only check for updates every n blocks
let current_block_number = self.client.upgrade().map_or(0, |c| c.block_number(BlockId::Latest).unwrap_or(0));
if current_block_number % cmp::max(self.update_policy.frequency, 1) != 0 {
return;
info!("The `updater_policy frequency` check is DISABLED!!!!!. This check is just diabled to test the updater and should be removed later, {}:{}", file!(), line!());
// return;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Cleanup/TODO?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yepp, only for manual testing

parity/main.rs Outdated
let same_name = exe_path
.as_ref()
.map_or(false, |p| {
p.file_stem().map_or(false, |n| n == "parity")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd make the executable name a const.

parity/main.rs Outdated
if !force_direct && !development && same_name {
// looks like we're not running ~/.parity-updates/parity when the user is expecting otherwise.
// Try to run the latest installed version of `parity`,
// upon failure it fails fall back into the locally installed version of `parity`
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/upon failure it fails fall back/upon failure it falls back/

// If we fail to run the updated parity then fallback to local version.
let latest_exe = latest_exe_path();
// `Path` to the latest downloaded binary
let latest_exe = latest_exe_path().ok();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Aren't we swallowing the error here? Is that what we want or do we want to print something useful?

Copy link
Collaborator Author

@niklasad1 niklasad1 Jun 12, 2018

Choose a reason for hiding this comment

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

Basically, it checks if we have a file named latest which in turn contains the latest known version of parity if the local version is not the latest. If latest doesn´t exist (it is None) then we run our locally installed version of parity!

It is used for this condition also traces before and after that already exist which should be sufficient IMO!

trace_main!("Starting up {} (force-direct: {}, development: {}, same-name: {})", std::env::current_exe().map(|x| format!("{}", x.display())).unwrap_or("<unknown>".to_owned()), force_direct, development, same_name);

// absolute path to the current `binary`
let exe_path = std::env::current_exe().ok();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should handle the error here and be very careful not to introduce vulnerabilities.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This "shouldn't" fail it's a wrapper on top of the underlying OS.
Do you want unwrap/expect here?

However, it's safe because if this fails we fall back to the locally installed version (or in other words don't spawn a child process)

parity/main.rs Outdated
File::open(update_path("latest")).and_then(|mut f| {
let mut exe = String::new();
trace!(target: "updater", "latest binary path: {:?}", f);
f.read_to_string(&mut exe).map(|_| update_path(&exe))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Am I reading this right? Are we reading the whole binary into a String? Can you help me understand why that is?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There are two files in .local/share/io.parity.ethereum-updates (path for *NIX-based systems):

  • latest - contains the name of the latest binary
  • the actual binary - some like parity-1.9.5-ff821daf1da42865f229aee35f2e74e7b2dd8db2

So if .local/share/io.parity.ethereum-updates/latest exists it will return the path the newest binary e.g, .local/share/io.parity.ethereum-updates/parity-1.9.5-ff821daf1da42865f229aee35f2e74e7b2dd8db2

Copy link
Collaborator

Choose a reason for hiding this comment

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

Changing the name of the variable from exe to exe_path would make it simple to understand :)

@5chdn
Copy link
Contributor

5chdn commented Jun 29, 2018

Test updater::tests::should_check_for_updates_with_configured_frequency fails.

@niklasad1
Copy link
Collaborator Author

@5chdn It is expected because I have hardcoded the version number in order to force an update!

@5chdn
Copy link
Contributor

5chdn commented Jul 2, 2018

So at what point this PR is reviewable/mergable?

@niklasad1
Copy link
Collaborator Author

niklasad1 commented Jul 3, 2018

@5chdn
The PR is still reviewable IMO but I did it like this because it's very hard to test the main loop in parity without mocking it in a test i.e, when it forks itself and spawns a child process and should fall-over to the local version when invalid cli args are entered!

So the hard-coded stuff will be reverted if @tomusdrw approved this approach otherwise I need to restructure updater_loop!

Copy link
Collaborator

@tomusdrw tomusdrw left a comment

Choose a reason for hiding this comment

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

Looks good, couple of minor grumbles! Sorry for a late review, I've missed the notification for that one.

parity/main.rs Outdated
File::open(update_path("latest")).and_then(|mut f| {
let mut exe = String::new();
trace!(target: "updater", "latest binary path: {:?}", f);
f.read_to_string(&mut exe).map(|_| update_path(&exe))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Changing the name of the variable from exe to exe_path would make it simple to understand :)

@@ -600,13 +611,15 @@ impl<O: OperationsClient, F: HashFetch, T: TimeProvider, R: GenRange> Updater<O,

// We rely on a secure state. Bail if we're unsure about it.
if self.client.upgrade().map_or(true, |c| !c.chain_info().security_level().is_full()) {
return;
info!("The `security_level` check is DISABLED!!!!! It is just diabled to test the updater-verification and should be removed later, {}:{}", file!(), line!());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we have a const DEBUG: bool = for testing? Then such code could be committed and would help others working on this in the future.

current_block_number,
latest.track.fork,
latest.fork);
latest.this_fork.map_or_else(|| "unreleased".into(), |f| format!("#{}", f)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should be unknown not unreleased since it's the fork name.

@5chdn 5chdn added A6-mustntgrumble 💦 Pull request has areas for improvement. The author need not address them before merging. and removed A0-pleasereview 🤓 Pull request needs code review. labels Jul 5, 2018
Copy link
Collaborator

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

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

Minor grumbles

Cargo.toml Outdated
@@ -106,6 +106,10 @@ deadlock_detection = ["parking_lot/deadlock_detection"]
# `valgrind --tool=massif /path/to/parity <parity params>`
# and `massif-visualizer` for visualization
memory_profiling = []
# use harcoded version 1.3.7 of parity to force an update
# and to manually test that parity fall-over the local version
Copy link
Collaborator

Choose a reason for hiding this comment

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

"…that parity reverts to the local version…"?

Cargo.toml Outdated
@@ -106,6 +106,10 @@ deadlock_detection = ["parking_lot/deadlock_detection"]
# `valgrind --tool=massif /path/to/parity <parity params>`
# and `massif-visualizer` for visualization
memory_profiling = []
# use harcoded version 1.3.7 of parity to force an update
# and to manually test that parity fall-over the local version
# in case of invalid or depricated command line arguments
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/depricated/deprecated/

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, this was sloppy of me!

[features]
# use harcoded version 1.3.7 of parity to force an update
# and to manually test that parity fall-over the local version
# in case of invalid or depricated command line arguments
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as above

@@ -28,3 +28,9 @@ rand = "0.4"
ethcore = { path = "../ethcore", features = ["test-helpers"] }
tempdir = "0.3"
matches = "0.1"

[features]
# use harcoded version 1.3.7 of parity to force an update
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is "magic" about v1.3.7? Might be worth mentioning in the comment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nothing, just old enough to be regarded as a critical update!

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd add a parenthesis like "(a version old enough to require a critical update, nothing special about it per se)" or something along those line. But up to you!

@5chdn
Copy link
Contributor

5chdn commented Jul 7, 2018

Could not compile wasm.

Why is that happening? Restarting CI

@niklasad1
Copy link
Collaborator Author

@5chdn I think I have to update the submodule in this PR, can check later!

niklasad1 and others added 7 commits July 8, 2018 22:27
* Introduce a new feature `test-updater` in order conditionally hardcode
the version number in parity in order to force an update
* This should only be used for debug/dev purposes
@5chdn
Copy link
Contributor

5chdn commented Jul 9, 2018

Restarted CI, seems unrelated.

@5chdn 5chdn added A8-looksgood 🦄 Pull request is reviewed well. and removed A6-mustntgrumble 💦 Pull request has areas for improvement. The author need not address them before merging. labels Jul 10, 2018
@5chdn 5chdn merged commit 6816f8b into master Jul 10, 2018
@5chdn 5chdn deleted the na-updater-verification branch July 10, 2018 10:17
dvdplm added a commit that referenced this pull request Jul 10, 2018
* master:
  Delete crates from parity-ethereum and fetch them from parity-common instead (#9083)
  Updater verification (#8787)
  Phrasing, precisions and typos in CLI help (#9060)
let prefix = vec![OsString::from("--can-restart"), OsString::from("--force-direct")];
let res = latest_exe_path().and_then(|exe| process::Command::new(exe)

Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure your editor trims whitespaces automatically

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Auto-updater upgradability verification
4 participants