Electron-builder: Electron-Updater Cannot Find Latest Releases or Update with Generic Nuts Server.

Created on 29 Apr 2017  Â·  7Comments  Â·  Source: electron-userland/electron-builder

electron-builder: 17.1.1
target: 'nsis'

Sorry for the long post.

I've been working on getting an app to autoupdate with electron-updater and nuts server. After reading much of the source code and trying most configs, I've landed here for help. Let me explain some of the approaches I have taken and the results from each.

I am building for both Mac and Windows. I was able to get Mac's autoupdate to work with electron's native autoUpdate module. However, building for nsis did not allow me to continue with that module for Windows. I have switched to electron-updater, building the app with electron-builder. Note, I am using Nuts as a release server, with a private Github repo connected (yes, GH_TOKEN is set).

Attempted publish config (1)

    "publish": [
      {
        "provider": "generic",
        "url": "https://example.herokuapp.com/download"
      },
      {
        "provider": "github",
      }
    ],

This is the suggested config in this issue. Hopeful, I worked with this for a while, but I continued to receive a "Cannot parse JSON" error when checkForUpdates ran. I narrowed down the issue to my config url. Since it is set to /download, electron-updater attempts to find the latest-mac.json at /download/latest-mac.json. Believe it or not, Nuts just serves the latest release at this route. The actual build file was being downloaded. So, it made sense that there was a JSON parse error, since it was trying to parse a .zip file!

Attempted publish config (2)

    "publish": [
      {
        "provider": "generic",
        "url": "https://example.herokuapp.com/download/latest"
      },
      {
        "provider": "github",
      }
    ],

After discovering the error in my first config, I set the url to /download/latest. Since now I knew electron-updater would add /latest-mac.json to my url, /download/latest/latest-mac.json correctly serves the latest-mac.json file from Github. I thought everything would be fixed, but I got errors after the update-available event saying the release url was invalid. Puzzled (because the latest-mac.json file generated looked correct), I decided to manually check the file by downloaded it from /download/latest/latest-mac.json.

The /download/latest/latest-mac.json file looked like this.

{
  "version": "0.10.0",
  "releaseDate": "2017-04-28T01:53:36.389Z",
  "url": "https://github.com/exampleowner/examplerepo/releases/download/v0.10.0/AppName-0.10.0-mac.zip"
}

See how the url points to the GitHub release? That confused me, because my local latest-mac.json was pointing to my release server, not GitHub. After further digging, I discovered that electron-builder creates a github folder with a custom latest-mac.json in there. That custom file is what's published to GitHub. So, this direct GitHub link may work for the majority of apps, but since my repo is private, I was getting "unavailable url" errors.

This seems like a bug? The GitHub version of latest-mac.json should not be uploaded if there is a generic provider. Only the generic latest-mac.json should be uploaded.

Even so, I figured it was easy enough to work around. I would just manually overwrite the latest-mac.json file uploaded to Github with my correct local latest-mac.json file. That cleared the "unavailable url" error, but there were still errors thrown. Looking at my "correct" latest-mac.json (the one generated in the root build directory by electron-builder), I saw that it was pointing to an invalid route on my release server.

Here's the root latest-mac.json file:

{
  "version": "0.10.0",
  "releaseDate": "2017-04-28T20:45:52.703Z",
  "url": "https://example.herokuapp.com/download/latest/AppName-0.10.0-mac.zip"
}

See the url field? On the Nuts server, that path leads to a 500 error. To get it to work, I had to remove the /latest path to turn it into this: https://example.herokuapp.com/download/AppName-0.10.0-mac.zip. That path correctly downloads the file.

This is a doable work-around, yet it removes the ability to successfully publish to GitHub with electron-updater. Instead, the originally uploaded github/latest-mac.json file must be removed from the GitHub release, and then this /latest-mac.json must be manually updated and then uploaded to the GitHub release. Not great, but not horrible. Ideally, since the config url must be set to /download/latest for the latest-mac.json to be found correctly on the Nuts server, there would be a way to alternately set an additional base url for the download url, which would be /download in this case.

Anyways, after clearing those errors, electron-updater could not read the file after it was downloaded by electron-updater.

When using electron's native autoUpdater module, I would manually set the feed url to the Nuts route https://example.herokuapp.com/update/${platform}/${version}. After the autoUpdater module downloaded this file, it would successfully unpack and updating the app.
However, after switching to electron-updater, I'm getting these "read" errors after the latest version is downloaded (running the app with DEBUG=electron-builder).

Wait for app ready
Checking for update
  electron-builder request: {
  electron-builder   "hostname": "example.herokuapp.com",
  electron-builder   "path": "/download/latest/latest-mac.json",
  electron-builder   "protocol": "https:",
  electron-builder   "headers": {
  electron-builder     "User-Agent": "electron-builder",
  electron-builder     "Cache-Control": "no-cache"
  electron-builder   }
  electron-builder } +0ms
  electron-builder Response status: 200 OK, request options: {
  electron-builder   "hostname": "example.herokuapp.com",
  electron-builder   "path": "/download/latest/latest-mac.json",
  electron-builder   "protocol": "https:",
  electron-builder   "headers": {
  electron-builder     "User-Agent": "electron-builder",
  electron-builder     "Cache-Control": "no-cache"
  electron-builder   }
  electron-builder } +13s
Found version 0.18.5 (url: https://example.herokuapp.com/download/AppName-0.10.0-mac.zip)
Downloading update from https://example.herokuapp.com/download/AppName-0.10.0-mac.zip
2017-04-28 16:40:26.548 AppName[46751:2396017] Download completed to: file:///Users/derekduncan/Library/Caches/com.appname.AppName.ShipIt/update.zcSpOeW/AppName-0.10.0-mac.zip
{ Error: ditto: Couldn't read PKZip signature
  preventDefault: [Function: preventDefault],
  sender:
   AutoUpdater {
     _events: { error: [Function], 'update-downloaded': [Function] },
     _eventsCount: 2 } }
Error: Error: ditto: Couldn't read PKZip signature

Specifically, see the Error: Error: ditto: Couldn't read PKZip signature. It could be related to #1492, but there isn't a solution there yet, so not sure.

So, I finally got electron-updater to find and download the new version, but it cannot read it. I obviously want to apply these changes to Windows auto updating as well, but I need to resolve these Mac errors first.

I'm not sure what to try next, as I've already read a lot of source code and tried all different configs. I need some help to continue using electron-updater, and I will gladly create a PR to add all my learnings to the docs once this is resolve.

Thanks for all your hard work.

backlog electron-updater feature help wanted

Most helpful comment

Nuts provider is planned and will be released soon.

All 7 comments

@derek-duncan Did you ever find a workaround or solution for this? I'm stuck at this step too where I'm flip-flopping between a Windows build working and a mac build working but neither at the same time. I'm using the exact setup are are/were (nuts, config)

Wondering if I should head another direction.

@tlackemann yep! I'll post the solution in the morning :)

@derek-duncan No worries! I ended up patching something together.

I ended up adding S3 as another publish option. Across the code, it looks something like this

// package.json
{
  "provider": "generic",
   "url": "https://example.herokuapp.com/download"
},
{
  "provider": "github"
},
{
  "provider": "s3",
  "bucket": "example-releases",
  "path": "${version}"
}
// wherever autoUpdater lives
const { platform } = process
    if (platform === 'darwin') {
      autoUpdater.setFeedURL({
        provider: 's3',
        bucket: 'example-releases',
      })
    }
  }

If the platform is Windows, updates come from Nuts/Windows.Squirrel. If macOS, I default to grabbing it from S3 since I encountered that same bug(?) with Nuts that you did.

There's a small manual step of moving the release files from the S3 folder into the main bucket but I'm okay with this for now (easy enough to write a Webhook into our API.)

Hope this helps although would be eager to hear your solution!

Nuts provider is planned and will be released soon.

@develar was the nuts provider released? Or is there a solution for this?

@kitze No. And in general no need — consider to use S3 (amazon, digital ocean) or GitHub.

If someone still looking for this solution, I use https://my-updater.herokuapp.com/download/latest as feed URL. Based on this route https://github.com/GitbookIO/nuts/blob/master/lib/nuts.js#L57 auto-updater will download latest-mac.yml automatically at https://my-updater.herokuapp.com/download/latest/latest-mac.yml.

Here's my snippet:

const { autoUpdater } = require('electron-updater')

app.on('ready', () => {
  autoUpdater.setFeedURL({
    provider: 'generic',
    url: 'https://my-updater.herokuapp.com/download/latest'
  })
  autoUpdater.checkForUpdatesAndNotify()
  ...
})
Was this page helpful?
0 / 5 - 0 ratings

Related issues

popod picture popod  Â·  3Comments

mstralka picture mstralka  Â·  3Comments

AidanNichol picture AidanNichol  Â·  3Comments

NPellet picture NPellet  Â·  3Comments

xingoxu picture xingoxu  Â·  3Comments