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

Building and Releasing using Channels #1182

Closed
consense opened this Issue Jan 27, 2017 · 48 comments

Comments

Projects
None yet
8 participants
@consense

consense commented Jan 27, 2017

  • Version: 11.2.4
  • Building on: win64

  • Target: win32/64

  • Aim: Allow users to receive alpha, beta or release builds via autoupdate. The type of channel would be set at runtime (depending on user-settings returned from a remote REST api). Generic server-side provider prefered. User set to channel alpha would receive alpha, beta and release updates, beta users beta and release, release users only release.

  • Problem: The process to accomplish the above may already be possible, but some information is spread across different ticket comments and at least for me not comprehensively understandable. In particular I am unsure whether my understanding describes the recommended workflow of electron-builder, seeing the project is (loving it) going through a lot of iterations in the last months. If you allow me to summarize my understanding of the necessary steps and accompanying questions:

  1. Build

    1. new version is pushed with app/package.json containing a version such as 0.12.1-alpha.1

    2. CI server could (in some external script) parse the version (e.g. 0.12.1-alpha.1) from app/package.json and execute the electron-builder build cli with channel-injecting parameter --em.build.publish.channel=alpha . This would result in alpha.yml file being created alongside of AppName 0.12.1-alpha.1.exe on the CI server.
      Question: Is electron-builder itself capable of recognizing from app/package.json that the version contains an alpha/beta prerelease tag and determine the correct publish.channel parameter itself without relying on a custom script for this?

    3. CI server uploads the yml and exe file to generic http host (e.g. using s3-deploy npm package)

    4. So S3 contains eventually a growing number of distribution versions (exe) and 3 yml files (alpha.yml, beta.yml and release.yml)

  2. Auto-update

    1. Electron app initializes autoupdater passing in the desired release channel at runtime. e.g. autoUpdater.setFeedURL({url: 'https://mys3bucketurl.com', channel: 'alpha'});

    2. Autoupdater fetches the yml corresponding to channel property passed into setFeedUrl from the generic http server.
      Autoupdater then checks, whether the version from the downloaded yml file is greater than the installed version (using semver.gt as in

      if (!isVersionGreaterThan(latestVersion, currentVersion)) {
      )
      Question: semver.gt prevents prerelease upgrades e.g. from 0.12.1-alpha.1 to 0.12.3-beta.1 (see https://github.com/npm/node-semver#prerelease-tags ). While the reasoning from semver guidelines regarding handling of prerelease tags makes sense for NPM, in this case autoupdater has no way of knowing that version 0.12.2 (which would be a valid update) exists.
      Also semver.gt will never allow jumping from a release version (e.g. 0.12.2) to a prerelease version (e.g. 0.12.3-alpha.1) which makes the whole channel concept pointless.
      Is this already solved or am I maybe making stuff more complicated than it needs to be? Or would I need to write a lambda function myself which performs this kind of semver.satisfies check and streams the exe file through from s3?

    3. Update is downloaded and event update-downloaded is being raised

edit: removed reference to cloudfront as it wasnt contributing to the core question

@develar

This comment has been minimized.

Member

develar commented Jan 27, 2017

Side note — s3 offers ssl, cloudfront not required.

@consense

This comment has been minimized.

consense commented Jan 27, 2017

Thanks develar, yes actually the reason for cloudfront was Chrome giving a warning upon download (untrusted content) which went away using the domain the electron app is also signed with via cloudfront.
Sorry for being misleading there.

@develar

This comment has been minimized.

Member

develar commented Jan 27, 2017

Is electron-builder itself capable of recognizing from app/package.json that the version contains an alpha/beta prerelease tag

Great and amazing idea. Will be implemented.

  1. Implement auto channel suggestion.
  2. Check semver logic, reported points are valid.

Thanks for clear feature request.

@develar

This comment has been minimized.

Member

develar commented Jan 31, 2017

@develar develar self-assigned this Apr 10, 2017

develar added a commit to develar/electron-builder that referenced this issue Apr 15, 2017

feat: automatically set channel to version prerelease component
BREAKING CHANGE: if app version is `0.12.1-alpha.1`, file `alpha.yml` will be generated instead of `latest.yml`

Close electron-userland#1182

develar added a commit to develar/electron-builder that referenced this issue Apr 15, 2017

feat: automatically set channel to version prerelease component
BREAKING CHANGE: if app version is `0.12.1-alpha.1`, file `alpha.yml` will be generated instead of `latest.yml`

Close electron-userland#1182

@develar develar closed this in 831186f Apr 15, 2017

@consense

This comment has been minimized.

consense commented May 4, 2017

@develar thanks a lot for implementing this. Works great.

One question if you allow:
considering the scenario I mentioned in the initial ticket description, that is someone on alpha channel should also get beta and release(=latest).
As far as I can see electron-updater is (of course) not aware of any [alpha|beta|latest].yml files residing in S3. So to reach the desired behavior I am randomly selecting one of the three channels when doing a regular (on timer) check for updates

autoUpdater.setFeedURL({
	url: 'https://releases.for-my-app.com',
	provider: 'generic',
	channel: releaseChannel // <-- randomly selected one out of [alpha|beta|latest]
});
autoUpdater.checkForUpdates();

This works fine but feels awkward so just to confirm - is there any better way possible currently?

Thanks a bunch

@Avien

This comment has been minimized.

Avien commented May 28, 2017

@consense @develar
If I want to have a dynamic url according to build env like:
production to:
https://www.something.com/update
and master to:
https://www.something.com/update-beta

What is the best way of doing it according to this issue?
I Cant understand exactly what I have to implement and where
Can I use the --em.build.publish.channel=beta to override the config?
ShouldI also be using the setFeedURL ?
Please advise, thanks

@Avien

This comment has been minimized.

Avien commented May 29, 2017

@develar Problem solved: looks likedetectUpdateChannel was not working for me because I was overriding the channel in the build publish configuration with the latest channel

Anyway I suggest to add proper documentation for it, its really unclear how to implement channels

@popod

This comment has been minimized.

Contributor

popod commented Aug 24, 2017

@develar what is the proper way to handle the scenario that is mentioned by @consense:

someone who is on alpha channel should also get beta and release(=latest) updated.

I want to allow my users to get "prerelease" marked as "beta". The channel (beta|latest) is defined in the application settings. I want thant the user who define "beta" channel get latest version too..

Example:
List of my releases: v0.1.0, v0.1.1, v0.1.2-beta.1, v0.1.2-beta.2, v0.1.2, ...
The user with channel "latest" should only get v0.1.0, v0.1.1 and v0.1.2
the user with the channel "beta" should get all this updates.

Is there any solution to handle this ? If yes what is it or what is the tricks ? Thanks

@develar

This comment has been minimized.

Member

develar commented Aug 25, 2017

@popod If you use GiHub, just one line — set allowPrerelease to true. Please see #1923

@popod

This comment has been minimized.

Contributor

popod commented Aug 25, 2017

@develar Thanks for reply, but I use a generic server for the auto-update. I think this is not possible to do this yet and this should be a nice option to add to the auto-updater..

I think this would be done by doing one of this:

  • add an option to set the url who return the latest version of all the channels
  • update latest.yml by adding all the channels version
  • ??

What do you think about implementing this or what is the workaround (with a generic auto-update server) ?

@develar

This comment has been minimized.

Member

develar commented Aug 25, 2017

@popod In case of generic server, you need to call setFeedUrl with modified value of channel.

No magic here. Updater simply uses channel to construct URL of update info file. No restrictions on version. If new downloaded update info reported that new version is 0.1.2-beta.2, and it is greater than current (0.1.1), update will be performed.

If you want to allow downgrade, set AppUpdater.allowDowngrade = true (see docs).

The only problem here — get generic config URL. i.e. when do you call setFeedUrl, you must provide FULL config, not only channel. Well, you can use undocumented and protected member configOnDisk (const oldConfig = await updater.configOnDisk.value (promise returned)).

I will make configOnDisk public, but you can access it in any case.

@develar

This comment has been minimized.

Member

develar commented Aug 25, 2017

@popod Will be amazing if you will write an article to docs when you will implement it ;)

@popod

This comment has been minimized.

Contributor

popod commented Aug 25, 2017

@develar I think I've understand how the auto-updater works, but how does it know that a new "stable" version is available if it check the "beta" channel ?

Example:

  • "stable" channel: v0.1.1, v0.1.2
  • "beta" channel: v0.1.2-beta.2, v0.1.2-beta.3

Here, there is no way to know that the v0.1.2 is available if the server check the "beta" channel.. If I'm right, this is not possible now with generic server ?

How could this be implemented ?

And yes, I will be happy to write some docs about that when I will have the solution ;)

@develar

This comment has been minimized.

Member

develar commented Aug 25, 2017

@popod Ouch, you are right.

@popod

This comment has been minimized.

Contributor

popod commented Aug 25, 2017

@develar and what about this trick ?

I take my latest example. The checked channel is "beta" and a new "stable" v0.1.2 is available.

If I copy the latest.yml file of version v0.1.2 to beta.yml. When the auto-updater will check the "beta" channel, it will now known that the new version is available. Is this a working workaround ?

If yes, I suggest to add an option channels in the provider config. This option accept an array of used channels (in this case ['stable'. 'beta']) and each time we package the application, the corresponding .yml files are created.

What do you think about that ?

@develar develar reopened this Aug 25, 2017

@develar

This comment has been minimized.

Member

develar commented Aug 25, 2017

I think it is ok to think about update info files as "feed". beta doesn't mean that this files contains ONLY beta releases, it means that it contains releases with whatever app developer considers as suitable for beta users.

Solution on client side is not good because it will increase HTTP request count and it costs money. Also, we should keep AppUpdater code small and simple as much as possible, because this code bundled with user app and so, it should be easily reviewable to ensure security.

I like you suggestion.

I don't like channels. We should create additional channel files only for stable. I suggest option in the Config (where detectUpdateChannel is defined). e.g. additionalUpdateChannelsIfStable Do you agree?

@popod

This comment has been minimized.

Contributor

popod commented Aug 25, 2017

We should create additional channel files only for stable.

I don't think so.. We need additional channel files following this :

  • none for "alpha" versions
  • "alpha" for "beta" versions (if we are on channel "alpha", we want "beta" versions too)
  • "alpha" and "beta" for "stable" versions
@develar

This comment has been minimized.

Member

develar commented Aug 25, 2017

How do you want to configure it? I mean — via config, via extraMetadata?

develar added a commit to develar/electron-builder that referenced this issue Oct 7, 2017

develar added a commit to develar/electron-builder that referenced this issue Oct 7, 2017

develar added a commit to develar/electron-builder that referenced this issue Oct 7, 2017

@develar develar closed this in 42f2ba1 Oct 7, 2017

@develar

This comment has been minimized.

Member

develar commented Oct 7, 2017

@popod Please try 19.35.0

@popod

This comment has been minimized.

Contributor

popod commented Oct 9, 2017

@develar a big thanks for this feature ! I will test it now ;)

  1. Should we set allowDowngrade = true to allow this works correctly ?
  2. Could you provide an easyer way to set autoUpdater.channel. This to allow us to set the wanted version at startup depending on the users settings (if they want beta or not) ?

Thanks !!!

@develar

This comment has been minimized.

Member

develar commented Oct 9, 2017

Should we set allowDowngrade = true to allow this works correctly ?

It is not strictly related. It is your own decision. If you user decided to change channel from beta to latest — yes, you need to set allowDowngrade = true Not clear for me — is it expected behaviour or not (from a user perspective).

Could you provide an easyer way to set autoUpdater.channel

Please file issue ;) Yes — if this method will be added, probably we can automatically set allowDowngrade to some value (to be discussed to what).

@popod

This comment has been minimized.

Contributor

popod commented Oct 9, 2017

Not clear for me — is it expected behaviour or not (from a user perspective).

I think that with generateUpdatesFilesForAllChannels = true, we want let the user choose if he wants beta versions or not. If he has beta versions and change to latest version, the application should automatically change the channel and allow "Downgrate". So I think that with generateUpdatesFilesForAllChannels = true, allowDowngrade should automatically been set to true too.

@jarrodek

This comment has been minimized.

jarrodek commented Oct 10, 2017

I can't get a head or tail out of it.
Can you please publish an example configuration of how to set up channels? I'm interested in standard setup like stable and beta. I'm using Github to publish the release. So far whether option I choose the app is always updating even if this is only a stable channel. So probably my config is bad but it's not very well documented so far.

@thomastvedt

This comment has been minimized.

thomastvedt commented Oct 26, 2017

Ok, this is really confusing.. how do I set up channels today?
Do i have to specify channel in version-string in package.json? (that worked), or can I set channel manually on CI server using something like --em.build.publish.channel=alpha as mentioned at the top here? --em.build is obsolete, I also tried --c.build.publish.channel=alpha and --c.publish.channel=alpha. The last one didnt throw an error, but channel was not set correctly..

@develar

This comment has been minimized.

Member

develar commented Oct 27, 2017

@jarrodek channels not supported for GitHub. Use prerelease instead.

@thomastvedt -c.publish.channel should work, but only if publish is specified in your config as one item.

@thomastvedt

This comment has been minimized.

thomastvedt commented Nov 1, 2017

I got -c.publish.channel to work after replacing my three seperate publish-sections for each platform with one publish section as you suggested :)

This didn't work with -c.publish.channel (maybe it should?):

"build": {
    "appId": "com.domain.app",
    "productName": "app",
    "copyright": "Copyright © 2017 ${author}",
    "artifactName": "${productName}-${channel}.${ext}",
    "compression": "normal",
    "detectUpdateChannel": "true",
    "files": [
      "build/**/*"
    ],
    "extraMetadata": {
      "main": "build/electron.js"
    },
    "mac": {
      "category": "public.app-category.business",
      "target": "default",
      "icon": "build/app-logo512.icns",
      "type": "development",
      "publish": {
        "provider": "s3"
      }
    }
}

But this worked:

"build": {
    "appId": "com.domain.app",
    "productName": "app",
    "copyright": "Copyright © 2017 ${author}",
    "artifactName": "${productName}-${channel}.${ext}",
    "compression": "normal",
    "detectUpdateChannel": "true",
    "files": [
      "build/**/*"
    ],
    "extraMetadata": {
      "main": "build/electron.js"
    },
    "publish": {
      "provider": "s3"
    },
    "mac": {
      "category": "public.app-category.business",
      "target": "default",
      "icon": "build/app-logo512.icns",
      "type": "development"
    }
}
@Johnz86

This comment has been minimized.

Johnz86 commented Dec 14, 2017

So this is definitely not helpful at all. I fail to make multi channel setup following this thread. What is more confusing is that official documentation points to this thread, like this thread should show clearly how to do it.

What does -c.publish.channel do for you? Does your CI dynamically generate and replace content of your package.json?

I have

npm 5.2.0
electron-builder 19.47.1

This is my build config

  "build": {
    "appId": "com.siemens.healthineers.imagepool.upload",
    "artifactName": "${productName}-${channel}.${ext}",
    "compression": "normal",
    "detectUpdateChannel": "true",        
    "files": [
      "assemble/**/*",
      "node_modules/**/*"
    ],
    "extraFiles": [
      "chrome_extensions",
      "app-config.json"
    ],
    "nsis": {
      "installerIcon": "./client/assets/images/upload.ico",
      "perMachine": true
    },
    "publish": {
      "provider": "s3",
      "bucket": "image-pool-upload-dev",
      "region": "eu-west-1",
      "acl": "public-read"
    },
    "win": {
      "icon": "./client/assets/images/upload.ico"
    }
  },

I have tried to run:

npm run package:multi -- --c.publish.channel=ci

when

    "package:multi": "electron-builder --win --ia32 --x64 --publish never",

This creates:

ci.yml
app-latest.exe

ci.yml points to app-latest.exe. When you run the app on update it goes to latest channel instead of ci channel.

How should I properly proceed, I only want to run a command for build with multiple channels?

@popod

This comment has been minimized.

Contributor

popod commented Dec 14, 2017

@Johnz86 Yes, I agree with you. I will write some doc and submit a PR when I will have some time...

Have you try to add generateUpdatesFilesForAllChannels: true ?

@develar

This comment has been minimized.

Member

develar commented Dec 14, 2017

@popod I pray for such docs :) You will be a hero :) I am too slow to implemented required for you functionality, but I hope now nothing stops you to help us :)

@Johnz86

This comment has been minimized.

Johnz86 commented Dec 14, 2017

@popod generateUpdatesFilesForAllChannels: true does not work. I did this:

  "build": {
    "appId": "com.siemens.healthineers.imagepool.upload",
    "artifactName": "${productName}-${channel}.${ext}",
    "compression": "normal",
    "generateUpdatesFilesForAllChannel": "true",
    "detectUpdateChannel": "true",

I get following error:

Error: Configuration is invalid.
 - configuration has an unknown property 'generateUpdatesFilesForAllChannel'. These properties are valid:
   object { afterPack?, apk?, appId?, appImage?, appx?, artifactName?, asar?, asarUnpack?, beforeBuild?, buildDependenciesFromSource?, buildVersion?, compression?, copyright?, deb?, detectUpdateChannel?, directories?, dmg?, electronCompile?, electronDist?, electronDownload?, electronVersion?, extends?, extraFiles?, extraMetadata?, extraResources?, fileAssociations?, files?, forceCodeSigning?, freebsd?, generateUpdatesFilesForAllChannels?, icon?, linux?, mac?, mas?, msi?, muonVersion?, nodeGypRebuild?, npmArgs?, npmRebuild?, npmSkipBuildFromSource?, nsis?, nsisWeb?, p5p?, pacman?, pkg?, portable?, productName?, protocols?, publish?, releaseInfo?, remoteBuild?, rpm?, snap?, squirrelWindows?, target?, win? }
@popod

This comment has been minimized.

Contributor

popod commented Dec 14, 2017

@Johnz86 yes this is generateUpdatesFilesForAllChannels with a 'S' at the end ;)

@Johnz86

This comment has been minimized.

Johnz86 commented Dec 14, 2017

@popod you are right, after I fixed the typo, it was executed. Still the build was with the same result ci.yml and productName-latest.exe

@Johnz86

This comment has been minimized.

Johnz86 commented Dec 14, 2017

So I found out the reason. The channel name is taken from version information in package.json. If there is no suffix then the channel value will be latest. If there is a value for example "0.30.4-ci", then your ${channel} will be "ci". The problem is if you want to include version information in the artifactName. You will get for example from "${name}-${version}-${channel}.${ext}" => "app-0.30.4-ci-ci.exe".

So to create a different channel I used:

{
...
"version": "0.30.4-ci",
...
  "build": {
    "appId": "com.example.some.app",
    "artifactName": "${name}-${version}.${ext}",
    "compression": "normal",
    "generateUpdatesFilesForAllChannels": "true",
    "detectUpdateChannel": "true",
    "files": [
      "assemble/**/*",
      "node_modules/**/*"
    ],
    "nsis": {
      "installerIcon": "./client/assets/images/upload.ico",
      "perMachine": true
    },
    "publish": {
      "provider": "s3",
      "bucket": "some-bucket",
    },
  }
...
}

I used command

npm run package -- --c.publish.channel=ci

and this produced:

ci.yml
app-0.30.4-ci.exe

and during the start of the app the auto-update correctly contacted ci.yml channel of the bucket.

This is very cumbersome. The channel information in publish section during build is ignored. And channel information must be included in version.

@popod

This comment has been minimized.

Contributor

popod commented Dec 16, 2017

@Johnz86 generateUpdatesFilesForAllChannels support only this channels: "latest", "beta", "alpha".

I'm not sure that I understand what you finally want, but if you only want to create a "ci" channel, generateUpdatesFilesForAllChannels is not for you.

If channel is ignored with npm run package -- --c.publish.channel=ci, try to remove the generateUpdatesFilesForAllChannels and if this does not works, open a new issue.

Hope this will help.

@develar

This comment has been minimized.

Member

develar commented Jan 26, 2018

@popod Anything else is required to be done before you can write docs about channels?

@popod

This comment has been minimized.

Contributor

popod commented Jan 26, 2018

@develar nothing for now.. I need to take some time to do that :) hope this will be soon.

@semireg

This comment has been minimized.

semireg commented Sep 18, 2018

I came here looking for a simple way to construct a beta channel where beta versions such as 1.0.0-beta.4 (the 4th beta of version 1.0.0) can be promoted to version 1.0.0 (stable). Is this possible?

@popod

This comment has been minimized.

Contributor

popod commented Sep 18, 2018

@semireg just follow this tutorial: https://www.electron.build/tutorials/release-using-channels.

You need to set generateUpdatesFilesForAllChannels to true in your package.json and if your application channel is 1.0.0-beta.4, 1.0.0 will automatically be installed when available.

@semireg

This comment has been minimized.

semireg commented Oct 3, 2018

@popod, thanks. I think I may have found a bug in this process. See #3360 for details.

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