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

Linux auto-update #1138

Closed
ssboisen opened this issue Jan 18, 2017 · 43 comments
Closed

Linux auto-update #1138

ssboisen opened this issue Jan 18, 2017 · 43 comments

Comments

@ssboisen
Copy link

  • Target: Linux

Electron-simple-updater supports appimage Linux Auto-updates, could this be ported to electron-builder?

@develar
Copy link
Member

develar commented Jan 18, 2017

Planned. There are a lot of more important issues.

@probonopd
Copy link

Also check AppImageUpdate for binary delta updates (= users only download a few MB rather than the whole thing each time).

@kwent
Copy link

kwent commented May 11, 2017

Do we an ETA for this ?

@develar
Copy link
Member

develar commented May 11, 2017

Probably this week (issues processed by user donations/requests/votes :)). But cannot promise anything.

@tiangolo
Copy link

Do you need help with this?

Is there any plan or work in progress?

I could help, but I don't want to duplicate work by not taking into account anything you may have already done.

@ghost
Copy link

ghost commented May 29, 2017

+1 on the above, I'd be happy to help.

@kwent
Copy link

kwent commented Jun 2, 2017

How can we help ? What is the plan ?

@EmadAdly
Copy link

me too

@rahilsondhi
Copy link

Happy to help as well 👷

@probonopd
Copy link

Since there seems to be high demand for AppImage updates, I'd like to offer my help too.

Update information that is needed for binary delta updates is described in the spec at
https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information

Of course this could be implemented from scratch, but the easier route would probably be to just use appimagetool which has all the needed capabilities already built in.

Since @develar wants to allow macOS and Windows users to generate Linux AppImages, a good first step would be to get appimagetool and its dependencies like mksquashfs and zsyncmake compiled for those platforms. https://github.com/AppImage/AppImageKit/

Volunteers, anyone?

Again, happy to help (but I don't run macOS or Windows at the moment). If you have questions, AppImage developers are on #AppImage on irc.freenode.net.

@probonopd
Copy link

probonopd commented Aug 8, 2017

By the way, if you are wondering what I mean by "binary delta updates", you can see it in action in this video:

https://www.youtube.com/watch?v=FqVAtHdK1N0

@develar
Copy link
Member

develar commented Aug 26, 2017

Next week differential updates will be finished for NSIS and I will be free from obligations. So, I will do support auto updates for Linux, as all you waiting it for a long time.

@develar
Copy link
Member

develar commented Aug 26, 2017

To make clear — AppImage is awesome. It is not archaic delta updates as Squirrel.Windows support. No, no. It saves hours and hours of developer time (yes, I am tired to answer to WTF, what remote releases I should use?). It is differential update. As Appx format in the Windows Store.

It saves bytes for developer because no need to download old release to build block map for a new release. It is very important for CI servers (no previous cache).

It saves bytes for client, because block map for one release is fully applicable for any release. Full update will be never downloaded again.

It saves developer time — nothing is added to existing workflow. Nothing to test. Nothing to deploy.

Yes — Google uses delta updates for Chrome. But what is good for a big company is not always good for a generic use case and often doesn't work. You want to develop your app, not to be release engineer. That's why Microsoft uses differential updates instead of delta for Windows Store.

So, stay tuned. Differential update for Windows will be in one week, for Linux — September.

@probonopd
Copy link

Didn't know that differential updates for UWP apps existed, but zsync (what powers AppImageUpdate) has been around forever and has been put to good use e.g., for Ubuntu ISOs for a long time.

@develar
Copy link
Member

develar commented Aug 26, 2017

@probonopd if I understand correctly, zsync builds blockmap for the whole zip file. Not for files in zip. As appx or our nsis does.

@probonopd
Copy link

In the case of AppImage, it would build it for the whole AppImage = ISO9660 or squashfs file.

@develar
Copy link
Member

develar commented Aug 27, 2017

99% that auto-update for Linux will use official AppImage solution, but not NSIS like solution. Yes — it will be cool to use LZMA instead of ZIP for AppImage to reduce size (~50%, "30-70%"), but there is a key difference — Linux kernel natively and transparently supports ZIP and so, AppImage zip is not just an intermediate container to unpack (as for portable windows target).

And we don't use zsync for NSIS, not only because lack of zsyncmake (https://github.com/salesforce/zsync4j — so, we can port it to node), but because AppX solution, I think, works better (we should operate by file, not the whole packed ZIP with lzma compression). Of course, AppX solution is just a variant of zsync idea (and our client will looks like as zsync client, but modify file not in the local file system, but file in the zip fs).

@probonopd
Copy link

99% that auto-update for Linux will use official AppImage solution

You may be interested to hear that @teras has successfully built appimagetool for macOS. See AppImage/AppImageKit#466 which will be merged soon.

@develar
Copy link
Member

develar commented Sep 10, 2017

Status: Delta updates for Windows is ready to use (https://github.com/electron-userland/electron-builder/releases/tag/v19.28.4). But after my vacation (21 September) I will fix another issue (because donated and so, has higher priority) and finally will implement auto-updates for Linux.

@develar
Copy link
Member

develar commented Oct 9, 2017

I am going to use NSIS implementation of differential update instead of AppImage.

  • users expect the same API and behaviour across platforms. Yes — we can (and will do) hide all implementation details under the hood, but in any case it will take additional amount of work and tests.
  • users expect the same functionality, that's why zsync-curl will be (if we will use AppImage update implementaion) used directly instead of AppImageUpdate. To ensure, that all our features like prerelease on GitHub or beta channels are fully applicable for Linux autoupdate. No doubt — on setFeedUrl we can apply new update information to AppImage, so, AppImage official way to update will be still applicable.
  • but what about proxy (or auto-detected by Chrome or set by user, since it is the same Electron API)? What about authentication headers? Or another custom headers? What about... yeach... that's why even low-level zsync-curl approach is not suitable for electron-builder users. Exactly the same reason why our improved and fixed auto update on macOS was not implemented on Squirrel.Mac side — it is not suitable for Electron app users. Better to implement once for all platforms. And only, only low-level specific code should be implemented platform-specific.
  • it is not about missing zsyncmake on mac — control file for zsync is very easy to generate (and there is implementation on Java).

Problem for now, is that NSIS currently uses static slicing, instead of dynamic sized chunks based on hashed content (content defined chunking). It works ok if blockmap computed per-file, but not good in case of AppImage, where zsync applied to whole AppImage file. In any case I need to fix it for NSIS (because of huge Electron executable (80 MB, changed dramatically after code sign)).

Decision is not final, depends on experiment results (how good is our differential updating compared to zsync). Great news is that this feature received donation, and so, I will work on it exclusively :)

@MaKleSoft
Copy link

@develar Great to hear you're going full-throttle on this! I don't know enough about the technology involved to weigh in but I'm sure you'll make the right decisions! Keep up the good work!

@probonopd
Copy link

probonopd commented Oct 9, 2017

@develar is there a description of how NSIS differential updates work? Do you think it might be possible and worthwhile to make NSIS differential updates an option officially supported in the AppImage standard? (Not promising anything, just trying to understand the pros and cons.)

Especially, how do NSIS differential updates know the URL to "update themselves" from?

@probonopd
Copy link

And, does the application author have to specifically produce delta files from each version to each version or does it work similar to zsync where you don't have to do this?

@develar
Copy link
Member

develar commented Oct 10, 2017

how do NSIS differential updates know the URL to "update themselves" from?

Note: Bintray is not described in this overview, because not suitable for a lot of users due to unacceptable pricing and so, not fully supported and not recommended by electron-builder anymore.

  1. electron-builder generates app-update.yml as part of the app (resources/app-update.yml).

    There are several publish providers — Bintray, Generic, GitHub (maybe private), S3 and DigitalOcean Spaces.

    For example, if you use GitHub:

    owner: develar
    repo: onshape-desktop-shell
    provider: github
  2. electron-builder generates and uploads update meta files. For generic provider you need to upload files somehow.

    For example, latest.yml for Windows nsis-web target (default nsis target currently doesn't support differential update, but planned):

    version: 1.0.1
    releaseDate: '2017-10-09T06:41:39.325Z'
    path: Test App ßW Web Setup 1.0.1.exe
    sha512: >-
      CTmj9nrloxLX03YuHHRODw0DwN5HL41zD0CAqKYcgyXnXzJ106QNRmuyKN7HoDYKjfmec1gv3Vh0tNVEDHmdCg==
    packages:
      x64:
        file: TestApp-1.0.1-x64.nsis.7z
        size: 34454700
        sha512: >-
          SOBydGzu9nmMAK85T/kGAJGMgzHUJ+wLDhhW6CxCkfpujWolFKEKadkMMpi9jZ0VBtNvFHe59orzyBiFbhzI3w==
        headerSize: 9982
        blockMapSize: 13408
    sha2: 9144ef58410cd6b2dd7fd0c4b9467e4cf2fc37d1bbb3155d6a4731f40ce7a975

    Here

    • sha2 because of backward compatibility.
    • sha512 is used to allow users to host updates on cheap HTTP instead of HTTPS (only update meta files must be served using HTTPS). Also, for package files, to ensure file integrity (if differential updater will produce incorrect file by error) and identity (because for blocks md5 is used).
    • headerSize - size of 7z archive header (to simplify updater, header is uncompressed (-mhc=off))
    • blockMapSize - size of package file block map (it is like zsync control file).
    • path - The relative path (by default) or full URL (can be manually edited to use absolute URLs (e.g. if you decided to migrate from S3 to Spaces)) to installer/package file (one web installer automatically select appropriate arch).

    Block map data is appended to the end of 7z archive as raw deflate compressed data (deflate, because only this compression method is supported by NodeJS natively). So, no additional file. Block map contains checksums not for the whole distributable file, but per application file (LZMA2 compressed data, solid archive is not used). And currently, static slicing is used (as in the AppX, and it is bad), not content defined (as in the zsync).

    latest.yml - here latest it is a channel name by default. If you publish 1.0.0-beta, prerelease component will be used as channel name — beta.yml will be generated/uploaded. You can also set generateUpdatesFilesForAllChannels to true and electron-builder will produce additional required channel files (e.g. for beta also alpha will be generated/uploaded, for latest both alpha and beta).

  3. electron-updater downloads latest.yml (app-update.yml is used to configure publish provider). Where latest it is the channel name by default. Can be set to any other channel name (except GitHub, where prerelease concept is used instead).

    For public GitHub atom feed is used to construct the channel file URL.

  4. Update is downloaded and installed depends on current version, downgrade policy, channel (allowPrerelease in case of GitHub) and staged rollouts (e.g. update will be not performed if user not belongs to confirmative bucket).

In short — NSIS differential update equals to zsync (again, currently, content defined chunking is not implemented, but will be — "depends on experiment results (how good is our differential updating compared to zsync").

So, as you can see, the only advantage of using zsync for us — content defined chunking. But in any case it should be implemented for NSIS, so it is not considered as "reinvent the wheel" :)

Also, and it is also important, our chunker understands the data. Block map computed per application file (7z format supported for now, in the future we can do the same for squashfs image). It is why initially NSIS chunked was implemented using static slicing :)

@MaKleSoft
Copy link

@develar Quick question: will this work for other distribution formats as well, like Debian packages?

@develar
Copy link
Member

develar commented Oct 10, 2017

@MaKleSoft AppX, Snap, Debian, rpm, pacman and so on are out of scope because you should use OS to update them. That's why AppImage is a default target — because it works on all Linux distro and updatable on all Linux too.

@MaKleSoft
Copy link

@develar Got it. Thanks for the quick response!

@TheAssassin
Copy link

@develar are you going to write the entire updater code in JS to make it portable, or are you building something subprocess based (e.g., calling NSIS processes)? I'm not quite sure what you're building.

@develar
Copy link
Member

develar commented Oct 10, 2017

@TheAssassin electron-updater differential updater for Windows Web Installer is already implemented and works in production.

  1. download full web-installer (~400 KB).

  2. electron-updater (JS code) reconstruct package file in a 7z format:

    1. signature copied as is
    2. stream data (non-solid 7z archive contains n lzma2 compressed blocks, where n is a number of files) downloaded using differential update. Opposite to zsync, updater has blockmaps ("zsync control files") not only for the new package file (located in the end of file (7z format allows to put any data after header, the same, I hope, will allow AppImage (not yet investigated))), but for old too (on install, block map copied to installed app directory).
    3. header (and blockmap) copied as is.
  3. and then simply calls downloaded installer (step 1) with --package-file=${packagePath}

The same will be implemented (if zsync will be not better (delta)) in case of AppImage (step 1 is missed, step 3 replaced to "integrate into system (new icons and so)").

@develar
Copy link
Member

develar commented Oct 12, 2017

Our CDC (content defined chunking) works ideally. In general, delta update size equals to n * dictSize where n it is a number of changed blocks (block size is equals to dict size). So, in case if NSIS, minimal delta size is 3-4 MB. Because of superb LZMA compression (https://github.com/electron-userland/electron-builder/releases/tag/v19.36.0).

For AppImage we don't use xz compression. Because XZ is much worse than 7z. Under the hood both uses LZMA2, but... filters, filters, filters... XZ is bad not only in terms of compression quality, but in terms of compression speed too (it the reason why we never use it even for xz files (deb) and instead delegates to 7z).

And in any case problem is that decompression of xz is slow and it leads to slow startup of app. Not often noticeable. And definitely, we don't want to compromise AppImage. You still can set in the appImage option compression to maximum (but please note — it doesn't make a lot of sense (only 1.5 MB in our tests)) — electron-builder doesn't restrict you to do so and doesn't check it at all — so, it is allowed to build one version with one compression and another version with another compression.

So, by default gzip compression is used. It is why Windows installer has size 34 MB, but Linux AppImage 52 MB. But because of that delta update size is very small — minimal ~100 KB.

File file has 10 changed blocks
Full: 51,738.34 KB, To download: 117.4 KB (0%)

Our one-click method autoUpdater.checkForUpdatesAndNotify() now supports Linux.

@probonopd
Copy link

So, would you say we shuold look into NSIS instead of zsync for AppImage in general?

@develar
Copy link
Member

develar commented Oct 12, 2017

electron-builder 19.37.0
electron-updater 2.12.0

Both versions are next.

checkForUpdatesAndNotify checks that APPIMAGE env is set, otherwise warning printed. So, it is safe to call this method if other distributable formats are also used.

@develar
Copy link
Member

develar commented Oct 12, 2017

“File file” in the log not a typo :) Just because the whole AppImage added to block map as file named ‚file‘ :)

@ashokgelal
Copy link

AutoUpdater now works great with AppImage except for one minor thing - the downloaded AppImage needs to be manually made executable (using chmod a+x). While this isn't a big deal, it could confuse non-savvy users. It would be nice if after copying the latest AppImage file it was made executable automatically.

@develar
Copy link
Member

develar commented Oct 15, 2017

It would be nice if after copying the latest AppImage file it was made executable automatically.

It is already executable. 247c18a#diff-0bb16ebe6bbc7bee3f53626ef72f6660R114

I have checked updater manualyl, not only using integration tests. Please provide steps to reproduce and file new issue.

@ashokgelal
Copy link

ashokgelal commented Oct 15, 2017

Hmmm... that's interesting. Not sure why but I got it working using the checkForUpdatesAndNotify() method. Before I was using checkForUpdates() and listening to update-downloaded event. Now the only thing that remains is that the old file is not deleted/replaced. Do the name of the new AppImage has to be same as that of the old one
as in the code below?

if (path.basename(installerPath) === path.basename(appImageFile)) {

@develar
Copy link
Member

develar commented Oct 22, 2017

Keep in mind — some breaking change may be done #2216

@gmanriqueUy
Copy link

I'm not sure if this is the place for asking this.
I'm implementing all the events (update-available, update-downloaded and download-progress, etc) to make the update process more friendly to my users.

I've made a progress bar which is working great on Windows with NSIS, but the event download-progress is never emitted on AppImage.

Is it supposed to be working or this is the expected behavior?

Thanks!

@anla-xu
Copy link

anla-xu commented Jul 14, 2021

我不确定这是不是问这个的地方。
我正在实施所有事件(update-availableupdate-downloadeddownload-progress等)以使更新过程对我的用户更友好。

我制作了一个进度条,它在带有 NSIS 的 Windows 上运行良好,但该事件download-progress从未在 AppImage 上发出。

它应该工作还是这是预期的行为?

谢谢!

hi ! I have the same problem. Have you solved it?

1 similar comment
@anla-xu
Copy link

anla-xu commented Jul 14, 2021

我不确定这是不是问这个的地方。
我正在实施所有事件(update-availableupdate-downloadeddownload-progress等)以使更新过程对我的用户更友好。

我制作了一个进度条,它在带有 NSIS 的 Windows 上运行良好,但该事件download-progress从未在 AppImage 上发出。

它应该工作还是这是预期的行为?

谢谢!

hi ! I have the same problem. Have you solved it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

14 participants