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:
Build
new version is pushed with app/package.json
containing a version such as 0.12.1-alpha.1
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?
CI server uploads the yml and exe file to generic http host (e.g. using s3-deploy npm package)
So S3 contains eventually a growing number of distribution versions (exe) and 3 yml files (alpha.yml
, beta.yml
and release.yml
)
Auto-update
Electron app initializes autoupdater passing in the desired release channel at runtime. e.g. autoUpdater.setFeedURL({url: 'https://mys3bucketurl.com', channel: 'alpha'});
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 https://github.com/electron-userland/electron-builder/blob/7c2973dda46434827fc43a1c330bbf9da3923053/packages/electron-updater/src/AppUpdater.ts#L156)
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?
Update is downloaded and event update-downloaded
is being raised
edit: removed reference to cloudfront as it wasnt contributing to the core question
Side note — s3 offers ssl, cloudfront not required.
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.
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.
Thanks for clear feature request.
semver works correctly — https://runkit.com/584689129842660013ba48de/58908629690e68001400de86
@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
@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
@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
@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
@popod If you use GiHub, just one line — set allowPrerelease
to true
. Please see https://github.com/electron-userland/electron-builder/issues/1923
@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:
What do you think about implementing this or what is the workaround (with a generic
auto-update server) ?
@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.
@popod Will be amazing if you will write an article to docs when you will implement it ;)
@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:
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 ;)
@popod Ouch, you are right.
@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 ?
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?
We should create additional channel files only for stable.
I don't think so.. We need additional channel files following this :
How do you want to configure it? I mean — via config, via extraMetadata?
I think your proposition is good !
I suggest option in the Config (where detectUpdateChannel is defined).
But with this label generateUpdatesFilesForAllChannels
?
generateUpdateFileForAllChannels
— electron-builder is not aware about your channels and using this property you will configure it. So, generateUpdateFileForChannels
and accept list of strings (channel name).
Yeach.... and now I realize your initial proposal about ability to specify several channels names in the channel
. Because this option just set what files will be produced. I think, no need yet another options, existing channel
should accept array.
@develar ahah yes.. But I think we have a problem.. by allowing users to set their own channels how to know the order (which channel overwrite the other -> stable overwrite beta). Because when we release a "beta" version, we do not overwrite the "stable" .yml file.
Solution:
generateUpdateFileForChannels
true|false and hardcode the .yml generated depending on the current application version. With this we only support "alpha", "beta" and "stable", I think this is enough for now..generateUpdateFileForChannels
Array of string and the order is important ? example : ['alpha-test', 'betatester', 'prod']
(but I think this is hard to support this ?!)We need to generate this .yml files depending on the current version we are building:
According to your comment, for now, I think boolean option as you have suggested generateUpdatesFilesForAllChannels
will really work. You just set it to true and that's all. electron-builder simply detect current channel and uses your logic to find additional channels names.
Ok, if no more comments, I will do it. Thanks a lot for cool proposal.
Yes, I think this is the simple (and better) way to do this !
Juste one more request : why not directly allow us to overwrite the autoUpdater.setFeedURL()
channel
parameter with autoUpdater.channel
like allowPrerelease
?
No problems ! Thanks you too for fast answers :)
Hi @develar. Is this improvement planned ? If yes, when do you think this will be released ? As requested, I will be happy to make some documentation about this when this will be implemented.
Hi @develar ! I saw you've begin something ;) Any news about this feature ?
generateUpdatesFilesForAllChannels
will be not applicable for GitHub (for GitHub should be pre-release way be used).
@popod Please try 19.35.0
@develar a big thanks for this feature ! I will test it now ;)
allowDowngrade = true
to allow this works correctly ?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 !!!
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).
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.
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.
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..
@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.
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"
}
}
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?
@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
?
@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 :)
@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? }
@Johnz86 yes this is generateUpdatesFilesForAllChannels
with a 'S' at the end ;)
@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
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.
@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.
@popod Anything else is required to be done before you can write docs about channels?
@develar nothing for now.. I need to take some time to do that :) hope this will be soon.
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?
@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.
@popod, thanks. I think I may have found a bug in this process. See https://github.com/electron-userland/electron-builder/issues/3360 for details.
Most helpful comment
Great and amazing idea. Will be implemented.
Thanks for clear feature request.