[platform] IDE update strategy reworked

- the concept of a 'new channel' dropped; no confusing "new channel" dialog anymore (IDEA-144121)
- updates are not restricted by a major version (IDEA-153386)
- ability added to target updates for specific builds on a server side
- UI tweaks (stable channel decription changed; ignored builds editing added)
This commit is contained in:
Roman Shevchenko
2016-04-06 12:15:39 +02:00
parent f44b6cc5d1
commit 1eedc6c253
34 changed files with 582 additions and 1457 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,11 +28,4 @@ public class UpdateStrategyCustomization {
public boolean forceEapUpdateChannelForEapBuilds() {
return true;
}
/**
* Whether the updater will allow patch updates to cross major version boundaries.
*/
public boolean allowMajorVersionUpdate() {
return false;
}
}
}
@@ -23,8 +23,6 @@ import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.LicensingFacade;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,7 +31,6 @@ import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.util.Date;
/**
* @author anna
@@ -76,25 +73,6 @@ public abstract class AbstractUpdateDialog extends DialogWrapper {
app.invokeLater(() -> app.restart(true));
}
protected void initLicensingInfo(@NotNull UpdateChannel channel, @SuppressWarnings("UnusedParameters") @NotNull BuildInfo build) {
LicensingFacade facade = LicensingFacade.getInstance();
if (facade != null) {
if (channel.getLicensing().equals(UpdateChannel.LICENSING_EAP)) {
myLicenseInfo = IdeBundle.message("updates.channel.bundled.key");
}
else {
Date buildDate = build.getReleaseDate();
Date expiration = facade.getLicenseExpirationDate();
if (buildDate != null && facade.isPerpetualForProduct(buildDate)) {
myLicenseInfo = IdeBundle.message("updates.fallback.build");
}
else if (expiration != null && expiration.after(new Date())) {
myLicenseInfo = IdeBundle.message("updates.subscription.active.till", DateFormatUtil.formatAboutDialogDate(expiration));
}
}
}
}
protected void configureMessageArea(@NotNull JEditorPane area) {
String messageBody = myEnableLink ? IdeBundle.message("updates.configure.label", ShowSettingsUtil.getSettingsMenuName()) : "";
configureMessageArea(area, messageBody, null, null);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ public enum ChannelStatus {
EAP("eap", "Early Access Program"),
MILESTONE("milestone", "Milestone Releases"),
BETA("beta", "Beta Releases or Public Previews"),
RELEASE("release", "New Major Version Releases");
RELEASE("release", "Stable Releases");
private final String myCode;
private final String myDisplayName;
@@ -53,4 +53,4 @@ public enum ChannelStatus {
public String toString() {
return myDisplayName;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.updateSettings.impl;
import com.intellij.openapi.util.BuildNumber;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -22,61 +23,48 @@ import java.util.Collections;
import java.util.List;
public class CheckForUpdateResult {
private final BuildInfo myNewBuildInSelectedChannel;
private final UpdateChannel myUpdatedChannel;
private final UpdateChannel myChannelToPropose;
private final List<String> myAllChannelIds;
private final UpdateStrategy.State myState;
private final BuildInfo myNewBuild;
private final UpdateChannel myUpdatedChannel;
private final Exception myError;
public CheckForUpdateResult(@Nullable BuildInfo newBuildInSelectedChannel,
@Nullable UpdateChannel updated,
@Nullable UpdateChannel channelToPropose,
@NotNull List<String> allChannelsIds) {
myNewBuildInSelectedChannel = newBuildInSelectedChannel;
myUpdatedChannel = updated;
myChannelToPropose = channelToPropose;
myAllChannelIds = allChannelsIds;
public CheckForUpdateResult(@Nullable BuildInfo newBuild, @Nullable UpdateChannel updatedChannel) {
myState = UpdateStrategy.State.LOADED;
myNewBuild = newBuild;
myUpdatedChannel = updatedChannel;
myError = null;
}
public CheckForUpdateResult(@NotNull UpdateStrategy.State state, @Nullable Exception e) {
myNewBuildInSelectedChannel = null;
myUpdatedChannel = null;
myChannelToPropose = null;
myAllChannelIds = Collections.emptyList();
myState = state;
myNewBuild = null;
myUpdatedChannel = null;
myError = e;
}
@Nullable
public BuildInfo getNewBuildInSelectedChannel() {
return myNewBuildInSelectedChannel;
}
@Nullable
public UpdateChannel getUpdatedChannel() {
return myUpdatedChannel;
}
@Nullable
public UpdateChannel getChannelToPropose() {
return myChannelToPropose;
}
@NotNull
public List<String> getAllChannelsIds() {
return myAllChannelIds;
}
@NotNull
public UpdateStrategy.State getState() {
return myState;
}
@Nullable
public BuildInfo getNewBuild() {
return myNewBuild;
}
@Nullable
public PatchInfo findPatchForBuild(@NotNull BuildNumber build) {
List<PatchInfo> patches = myNewBuild != null ? myNewBuild.getPatches() : Collections.emptyList();
return patches.stream().filter(p -> p.isAvailable() && p.getFromBuild().compareTo(build) == 0).findFirst().orElse(null);
}
@Nullable
public UpdateChannel getUpdatedChannel() {
return myUpdatedChannel;
}
@Nullable
public Exception getError() {
return myError;
}
}
}
@@ -1,101 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.updateSettings.impl;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.ui.BrowserHyperlinkListener;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.Objects;
/**
* @author yole
*/
class NewChannelDialog extends AbstractUpdateDialog {
private final UpdateChannel myChannel;
private final BuildInfo myLatestBuild;
public NewChannelDialog(@NotNull UpdateChannel channel) {
super(false);
myChannel = channel;
myLatestBuild = channel.getLatestBuild();
assert myLatestBuild != null;
initLicensingInfo(myChannel, myLatestBuild);
init();
}
@Override
protected JComponent createCenterPanel() {
return new NewChannelPanel().myPanel;
}
@NotNull
@Override
protected Action[] createActions() {
return new Action[]{
getOKAction(),
new AbstractAction(IdeBundle.message("updates.remind.later.button")) {
@Override
public void actionPerformed(ActionEvent e) {
UpdateSettings.getInstance().forgetChannelId(myChannel.getId());
doCancelAction();
}
},
getCancelAction()
};
}
@Override
protected String getOkButtonText() {
return IdeBundle.message("updates.more.info.button");
}
@Override
protected String getCancelButtonText() {
return IdeBundle.message("updates.ignore.update.button");
}
@Override
protected void doOKAction() {
BrowserUtil.browse(Objects.requireNonNull(myChannel.getHomePageUrl(), "missing URL for channel " + myChannel.getId()));
super.doOKAction();
}
private class NewChannelPanel {
private JPanel myPanel;
private JEditorPane myMessageArea;
private JEditorPane myLicenseArea;
private NewChannelPanel() {
String message = IdeBundle.message("updates.channel.name.message", myChannel.getName(), myLatestBuild.getMessage());
configureMessageArea(myMessageArea, message, null, BrowserHyperlinkListener.INSTANCE);
if (myLicenseInfo != null) {
configureMessageArea(myLicenseArea, myLicenseInfo, null, BrowserHyperlinkListener.INSTANCE);
}
else {
myLicenseArea.setVisible(false);
}
}
}
}
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.updateSettings.impl.NewChannelDialog.NewChannelPanel">
<grid id="27dc6" binding="myPanel" layout-manager="GridBagLayout">
<constraints>
<xy x="20" y="20" width="515" height="56"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1f0d5" class="javax.swing.JEditorPane" binding="myMessageArea">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="150" height="50"/>
</grid>
<gridbag weightx="1.0" weighty="0.0"/>
</constraints>
<properties>
<contentType value="text/html"/>
</properties>
</component>
<component id="63fa1" class="javax.swing.JEditorPane" binding="myLicenseArea">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="150" height="50"/>
</grid>
<gridbag top="10" left="0" bottom="0" right="0" weightx="0.0" weighty="0.0"/>
</constraints>
<properties>
<contentType value="text/html"/>
</properties>
</component>
<vspacer id="e7d5c">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<gridbag weightx="0.0" weighty="0.0"/>
</constraints>
</vspacer>
</children>
</grid>
</form>
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import javax.swing.*;
* @author pti
*/
class NoUpdatesDialog extends AbstractUpdateDialog {
protected NoUpdatesDialog(boolean enableLink) {
NoUpdatesDialog(boolean enableLink) {
super(enableLink);
init();
}
@@ -57,4 +57,4 @@ class NoUpdatesDialog extends AbstractUpdateDialog {
configureMessageArea(myMessageArea);
}
}
}
}
@@ -38,7 +38,7 @@ class PluginUpdateInfoDialog extends AbstractUpdateDialog {
private final Collection<PluginDownloader> myUploadedPlugins;
private final boolean myPlatformUpdate;
public PluginUpdateInfoDialog(Collection<PluginDownloader> uploadedPlugins, boolean enableLink) {
PluginUpdateInfoDialog(Collection<PluginDownloader> uploadedPlugins, boolean enableLink) {
super(enableLink);
myUploadedPlugins = uploadedPlugins;
myPlatformUpdate = false;
@@ -37,9 +37,7 @@ import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization
import com.intellij.openapi.util.ActionCallback
import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.SystemInfo
@@ -56,7 +54,6 @@ import com.intellij.util.ui.UIUtil
import com.intellij.xml.util.XmlStringUtil
import org.apache.http.client.utils.URIBuilder
import org.jdom.JDOMException
import org.jetbrains.annotations.Contract
import java.io.File
import java.io.IOException
import java.net.URISyntaxException
@@ -133,9 +130,7 @@ object UpdateChecker {
val result = checkPlatformUpdate(updateSettings)
if (manualCheck && result.state == UpdateStrategy.State.LOADED) {
val settings = UpdateSettings.getInstance()
settings.saveLastCheckedInfo()
settings.setKnownChannelIds(result.allChannelsIds)
UpdateSettings.getInstance().saveLastCheckedInfo()
}
else if (result.state == UpdateStrategy.State.CONNECTION_ERROR) {
val e = result.error
@@ -149,24 +144,16 @@ object UpdateChecker {
indicator?.text = IdeBundle.message("updates.checking.plugins")
val buildNumber: BuildNumber? = result.newBuild?.apiVersion
val incompatiblePlugins: MutableCollection<IdeaPluginDescriptor>? = if (buildNumber != null) HashSet<IdeaPluginDescriptor>() else null
val updatedPlugins: Collection<PluginDownloader>?
val incompatiblePlugins: MutableCollection<IdeaPluginDescriptor>?
if (newChannelReady(result.channelToPropose)) {
updatedPlugins = null
incompatiblePlugins = null
try {
updatedPlugins = checkPluginsUpdate(updateSettings, indicator, incompatiblePlugins, buildNumber)
}
else {
val buildNumber: BuildNumber? = result.newBuildInSelectedChannel?.apiVersion
incompatiblePlugins = if (buildNumber != null) HashSet<IdeaPluginDescriptor>() else null
try {
updatedPlugins = checkPluginsUpdate(updateSettings, indicator, incompatiblePlugins, buildNumber)
}
catch (e: IOException) {
showErrorMessage(manualCheck, IdeBundle.message("updates.error.connection.failed", e.message))
return
}
catch (e: IOException) {
showErrorMessage(manualCheck, IdeBundle.message("updates.error.connection.failed", e.message))
return
}
// show result
@@ -215,10 +202,7 @@ object UpdateChecker {
return CheckForUpdateResult(UpdateStrategy.State.NOTHING_LOADED, null)
}
val appInfo = ApplicationInfo.getInstance()
val majorVersion = Integer.parseInt(appInfo.majorVersion)
val customization = UpdateStrategyCustomization.getInstance()
val strategy = UpdateStrategy(majorVersion, appInfo.build, updateInfo, settings, customization)
val strategy = UpdateStrategy(ApplicationInfo.getInstance().build, updateInfo, settings)
return strategy.checkForUpdates()
}
@@ -362,11 +346,6 @@ object UpdateChecker {
}
}
@Contract("null -> false")
private fun newChannelReady(channelToPropose: UpdateChannel?): Boolean {
return channelToPropose?.getLatestBuild() != null
}
private fun showUpdateResult(project: Project?,
checkForUpdateResult: CheckForUpdateResult,
updateSettings: UpdateSettings,
@@ -374,14 +353,14 @@ object UpdateChecker {
incompatiblePlugins: Collection<IdeaPluginDescriptor>?,
enableLink: Boolean,
alwaysShowResults: Boolean) {
val channelToPropose = checkForUpdateResult.channelToPropose
val updatedChannel = checkForUpdateResult.updatedChannel
val latestBuild = checkForUpdateResult.newBuildInSelectedChannel
val newBuild = checkForUpdateResult.newBuild
if (updatedChannel != null && latestBuild != null) {
if (updatedChannel != null && newBuild != null) {
val runnable = {
val patch = checkForUpdateResult.findPatchForBuild(ApplicationInfo.getInstance().build)
val forceHttps = updateSettings.canUseSecureConnection()
UpdateInfoDialog(updatedChannel, latestBuild, enableLink, forceHttps, updatedPlugins, incompatiblePlugins).show()
UpdateInfoDialog(updatedChannel, newBuild, patch, enableLink, forceHttps, updatedPlugins, incompatiblePlugins).show()
}
if (alwaysShowResults) {
@@ -392,28 +371,6 @@ object UpdateChecker {
showNotification(project, message, runnable, NotificationUniqueType.UPDATE_IN_CHANNEL)
}
}
else if (newChannelReady(channelToPropose)) {
val runnable = {
val dialog = NewChannelDialog(channelToPropose!!)
dialog.show()
// once we informed that new product is available (when new channel was detected), remember the fact
if (dialog.exitCode == DialogWrapper.CANCEL_EXIT_CODE &&
checkForUpdateResult.state == UpdateStrategy.State.LOADED &&
!updateSettings.knownChannelsIds.contains(channelToPropose.id)) {
val newIds = ArrayList(updateSettings.knownChannelsIds)
newIds.add(channelToPropose.id)
updateSettings.setKnownChannelIds(newIds)
}
}
if (alwaysShowResults) {
runnable.invoke()
}
else {
val message = IdeBundle.message("updates.new.version.available", ApplicationNamesInfo.getInstance().fullProductName)
showNotification(project, message, runnable, NotificationUniqueType.NEW_CHANNEL)
}
}
else if (updatedPlugins != null && !updatedPlugins.isEmpty()) {
val runnable = { PluginUpdateInfoDialog(updatedPlugins, enableLink).show() }
@@ -15,9 +15,9 @@
*/
package com.intellij.openapi.updateSettings.impl
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.BuildRange
import com.intellij.openapi.util.SystemInfo
import org.jdom.Element
import org.jdom.JDOMException
@@ -27,23 +27,15 @@ import java.util.*
class UpdatesInfo(node: Element) {
private val products = node.getChildren("product").map { Product(it) }
val productsCount: Int
get() = products.size
fun getProduct(code: String): Product? = products.find { it.hasCode(code) }
operator fun get(code: String): Product? = products.find { code in it.codes }
}
class Product(node: Element) {
val name: String = node.getAttributeValue("name") ?: throw JDOMException("product.name missing")
val name: String = node.getAttributeValue("name") ?: throw JDOMException("product@name missing")
val codes: Set<String> = node.getChildren("code").map { it.value.trim() }.toSet()
val channels: List<UpdateChannel> = node.getChildren("channel").map { UpdateChannel(it) }
private val codes = node.getChildren("code").map { it.value.trim() }.toSet()
fun hasCode(code: String): Boolean = codes.contains(code)
fun findUpdateChannelById(id: String): UpdateChannel? = channels.find { it.id == id }
fun getAllChannelIds(): List<String> = channels.map { it.id }
override fun toString() = codes.firstOrNull() ?: "-"
}
class UpdateChannel(node: Element) {
@@ -52,74 +44,50 @@ class UpdateChannel(node: Element) {
const val LICENSING_PRODUCTION = "production"
}
val id: String = node.getAttributeValue("id") ?: throw JDOMException("channel.id missing")
val name: String = node.getAttributeValue("name") ?: throw JDOMException("channel.name missing")
val id: String = node.getAttributeValue("id") ?: throw JDOMException("channel@id missing")
val status: ChannelStatus = ChannelStatus.fromCode(node.getAttributeValue("status"))
val licensing: String = node.getAttributeValue("licensing", LICENSING_PRODUCTION)
val majorVersion: Int = node.getAttributeValue("majorVersion")?.toInt() ?: -1
val homePageUrl: String? = node.getAttributeValue("url")
val feedbackUrl: String? = node.getAttributeValue("feedback")
val evalDays: Int = node.getAttributeValue("evalDays")?.toInt() ?: 30
private val builds = node.getChildren("build").map { BuildInfo(it) }
val builds: List<BuildInfo> = node.getChildren("build").map { BuildInfo(it) }
fun getLatestBuild(): BuildInfo? = latestBuild(builds)
fun getLatestBuild(baseline: Int): BuildInfo? = latestBuild(builds.filter { it.number.baselineVersion == baseline })
private fun latestBuild(builds: List<BuildInfo>) =
builds.fold(null as BuildInfo?) { best, candidate -> if (best == null || best.compareTo(candidate) < 0) candidate else best }
override fun toString() = id
}
class BuildInfo(node: Element) : Comparable<BuildInfo> {
val number: BuildNumber = BuildNumber.fromString(node.getAttributeValue("number") ?: throw JDOMException("build.number missing"))
val apiVersion: BuildNumber = node.getAttributeValue("apiVersion")?.let { BuildNumber.fromString(it, number.productCode) } ?: number
class BuildInfo(node: Element) {
val number: BuildNumber = BuildNumber.fromString(node.getAttributeValue("number") ?: throw JDOMException("build@number missing"))
val apiVersion: BuildNumber = BuildNumber.fromString(node.getAttributeValue("apiVersion"), number.productCode) ?: number
val version: String = node.getAttributeValue("version") ?: ""
val message: String = node.getChild("message")?.value ?: ""
val releaseDate: Date? = node.getAttributeValue("releaseDate")?.let {
try { SimpleDateFormat("yyyyMMdd", Locale.US).parse(it) } // same as the 'majorReleaseDate' in ApplicationInfo.xml
val releaseDate: Date? = parseDate(node.getAttributeValue("releaseDate"))
val target: BuildRange? = BuildRange.fromStrings(node.getAttributeValue("targetSince"), node.getAttributeValue("targetUntil"))
val buttons: List<ButtonInfo> = node.getChildren("button").map { ButtonInfo(it) }
val patches: List<PatchInfo> = node.getChildren("patch").map { PatchInfo(it) }
private fun parseDate(value: String?): Date? = value?.let {
try {
SimpleDateFormat("yyyyMMdd", Locale.US).parse(it) // same as the 'majorReleaseDate' in ApplicationInfo.xml
}
catch (e: ParseException) {
Logger.getInstance(BuildInfo::class.java).info("Failed to parse build release date " + it)
null
}
}
val buttons: List<ButtonInfo> = node.getChildren("button").map { ButtonInfo(it) }
private val patches = node.getChildren("patch").map { PatchInfo(it) }
/**
* Returns -1 if version information is missing or does not match to expected format "majorVer.minorVer"
*/
val majorVersion: Int
get() {
val dotIndex = version.indexOf('.')
if (dotIndex > 0) {
try {
return version.substring(0, dotIndex).toInt()
}
catch (ignored: NumberFormatException) { }
}
return -1
}
fun findPatchForCurrentBuild(): PatchInfo? = findPatchForBuild(ApplicationInfo.getInstance().build)
fun findPatchForBuild(currentBuild: BuildNumber): PatchInfo? =
patches.find { it.isAvailable && it.fromBuild.asStringWithoutProductCode() == currentBuild.asStringWithoutProductCode() }
override fun compareTo(other: BuildInfo): Int = number.compareTo(other.number)
override fun toString(): String = "BuildInfo(number=$number)"
override fun toString() = "${number}/${version}"
}
class ButtonInfo(node: Element) {
val name: String = node.getAttributeValue("name") ?: throw JDOMException("button.name missing")
val url: String = node.getAttributeValue("url") ?: throw JDOMException("button.url missing")
val name: String = node.getAttributeValue("name") ?: throw JDOMException("button@name missing")
val url: String = node.getAttributeValue("url") ?: throw JDOMException("button@url missing")
val isDownload: Boolean = node.getAttributeValue("download") != null // a button marked with this attribute is hidden when a patch is available
override fun toString() = name
}
class PatchInfo(node: Element) {
val fromBuild: BuildNumber = BuildNumber.fromString(node.getAttributeValue("from") ?: throw JDOMException("patch.from missing"))
val fromBuild: BuildNumber = BuildNumber.fromString(node.getAttributeValue("from") ?: throw JDOMException("patch@from missing"))
val size: String? = node.getAttributeValue("size")
val isAvailable: Boolean = node.getAttributeValue("exclusions")?.split(",")?.none { it.trim() == osSuffix } ?: true
val isAvailable: Boolean = node.getAttributeValue("exclusions")?.splitToSequence(",")?.none { it.trim() == osSuffix } ?: true
val osSuffix: String
get() = if (SystemInfo.isWindows) "win" else if (SystemInfo.isMac) "mac" else if (SystemInfo.isUnix) "unix" else "unknown"
@@ -29,16 +29,20 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.ui.JBColor;
import com.intellij.ui.LicensingFacade;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
@@ -48,25 +52,26 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
private final UpdateChannel myUpdatedChannel;
private final boolean myForceHttps;
private final Collection<PluginDownloader> myUpdatedPlugins;
private final BuildInfo myLatestBuild;
private final BuildInfo myNewBuild;
private final PatchInfo myPatch;
private final boolean myWriteProtected;
protected UpdateInfoDialog(@NotNull UpdateChannel channel,
@NotNull BuildInfo latestBuild,
boolean enableLink,
boolean forceHttps,
Collection<PluginDownloader> updatedPlugins,
Collection<IdeaPluginDescriptor> incompatiblePlugins) {
UpdateInfoDialog(@NotNull UpdateChannel channel,
@NotNull BuildInfo newBuild,
@Nullable PatchInfo patch,
boolean enableLink,
boolean forceHttps,
Collection<PluginDownloader> updatedPlugins,
Collection<IdeaPluginDescriptor> incompatiblePlugins) {
super(enableLink);
myUpdatedChannel = channel;
myForceHttps = forceHttps;
myUpdatedPlugins = updatedPlugins;
myLatestBuild = latestBuild;
myPatch = myLatestBuild.findPatchForCurrentBuild();
myNewBuild = newBuild;
myPatch = patch;
myWriteProtected = myPatch != null && !new File(PathManager.getHomePath()).canWrite();
getCancelAction().putValue(DEFAULT_ACTION, Boolean.TRUE);
initLicensingInfo(myUpdatedChannel, myLatestBuild);
initLicensingInfo(myUpdatedChannel, myNewBuild);
init();
if (incompatiblePlugins != null && !incompatiblePlugins.isEmpty()) {
@@ -80,6 +85,25 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
}
}
private void initLicensingInfo(@NotNull UpdateChannel channel, @NotNull BuildInfo build) {
LicensingFacade facade = LicensingFacade.getInstance();
if (facade != null) {
if (channel.getLicensing().equals(UpdateChannel.LICENSING_EAP)) {
myLicenseInfo = IdeBundle.message("updates.channel.bundled.key");
}
else {
Date buildDate = build.getReleaseDate();
Date expiration = facade.getLicenseExpirationDate();
if (buildDate != null && facade.isPerpetualForProduct(buildDate)) {
myLicenseInfo = IdeBundle.message("updates.fallback.build");
}
else if (expiration != null && expiration.after(new Date())) {
myLicenseInfo = IdeBundle.message("updates.subscription.active.till", DateFormatUtil.formatAboutDialogDate(expiration));
}
}
}
}
@Override
protected JComponent createCenterPanel() {
return new UpdateInfoPanel().myPanel;
@@ -105,7 +129,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
});
}
List<ButtonInfo> buttons = myLatestBuild.getButtons();
List<ButtonInfo> buttons = myNewBuild.getButtons();
if (buttons.isEmpty()) {
actions.add(new AbstractAction(IdeBundle.message("updates.more.info.button")) {
@Override
@@ -125,7 +149,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
actions.add(new AbstractAction(IdeBundle.message("updates.ignore.update.button")) {
@Override
public void actionPerformed(ActionEvent e) {
String build = myLatestBuild.getNumber().asStringWithoutProductCode();
String build = myNewBuild.getNumber().asStringWithoutProductCode();
UpdateSettings.getInstance().getIgnoredBuildNumbers().add(build);
doCancelAction();
}
@@ -143,7 +167,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
private void downloadPatchAndRestart() {
try {
UpdateChecker.installPlatformUpdate(myPatch, myLatestBuild.getNumber(), myForceHttps);
UpdateChecker.installPlatformUpdate(myPatch, myNewBuild.getNumber(), myForceHttps);
if (myUpdatedPlugins != null && !myUpdatedPlugins.isEmpty()) {
new PluginUpdateInfoDialog(getContentPanel(), myUpdatedPlugins).show();
@@ -196,7 +220,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
ApplicationInfo appInfo = ApplicationInfo.getInstance();
ApplicationNamesInfo appNames = ApplicationNamesInfo.getInstance();
String message = myLatestBuild.getMessage();
String message = myNewBuild.getMessage();
final String fullProductName = appNames.getFullProductName();
if (StringUtil.isEmpty(message)) {
message = IdeBundle.message("updates.new.version.available", fullProductName);
@@ -217,7 +241,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
appInfo.getBuild().asStringWithoutProductCode()
)
);
myNewVersion.setText(formatVersion(myLatestBuild.getVersion(), myLatestBuild.getNumber().asStringWithoutProductCode()));
myNewVersion.setText(formatVersion(myNewBuild.getVersion(), myNewBuild.getNumber().asStringWithoutProductCode()));
if (myPatch != null && !StringUtil.isEmptyOrSpaces(myPatch.getSize())) {
myPatchInfo.setText(myPatch.getSize() + " MB");
@@ -38,7 +38,6 @@ import java.util.List;
public class UpdateSettings implements PersistentStateComponent<UpdateSettings.State>, UserUpdateSettings {
public static class State {
@CollectionBean public final List<String> pluginHosts = new SmartList<String>();
@CollectionBean public final List<String> knownUpdateChannels = new SmartList<String>();
@CollectionBean public final List<String> ignoredBuildNumbers = new SmartList<String>();
public boolean CHECK_NEEDED = true;
@@ -98,21 +97,6 @@ public class UpdateSettings implements PersistentStateComponent<UpdateSettings.S
}
@NotNull
@Override
public List<String> getKnownChannelsIds() {
return new ArrayList<String>(myState.knownUpdateChannels);
}
@Override
public void setKnownChannelIds(@NotNull List<String> ids) {
myState.knownUpdateChannels.clear();
myState.knownUpdateChannels.addAll(ids);
}
public void forgetChannelId(String id) {
myState.knownUpdateChannels.remove(id);
}
@Override
public List<String> getIgnoredBuildNumbers() {
return myState.ignoredBuildNumbers;
@@ -150,6 +134,7 @@ public class UpdateSettings implements PersistentStateComponent<UpdateSettings.S
return myState.SECURE_CONNECTION && NetUtils.isSniEnabled();
}
//<editor-fold desc="Deprecated stuff.">
/** @deprecated use {@link #getSelectedChannelStatus()} (to be removed in IDEA 17) */
@SuppressWarnings("unused")
public String getUpdateChannelType() {
@@ -161,4 +146,5 @@ public class UpdateSettings implements PersistentStateComponent<UpdateSettings.S
public void setUpdateChannelType(@NotNull String value) {
myState.UPDATE_CHANNEL_TYPE = value;
}
//</editor-fold>
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@ package com.intellij.openapi.updateSettings.impl;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
@@ -26,11 +28,14 @@ import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.labels.ActionLink;
import com.intellij.util.net.NetUtils;
import com.intellij.util.text.DateFormatUtil;
import org.jetbrains.annotations.NotNull;
@@ -40,6 +45,7 @@ import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.List;
/**
* @author pti
@@ -146,13 +152,14 @@ public class UpdateSettingsConfigurable extends BaseConfigurable implements Sear
private final UpdateSettings mySettings;
private JPanel myPanel;
private JCheckBox myCheckForUpdates;
private JComboBox myUpdateChannels;
private JComboBox<ChannelStatus> myUpdateChannels;
private JButton myCheckNow;
private JBLabel myChannelWarning;
private JCheckBox myUseSecureConnection;
private JLabel myBuildNumber;
private JLabel myVersionNumber;
private JLabel myLastCheckedDate;
@SuppressWarnings("unused") private ActionLink myIgnoredBuildsLink;
public UpdatesSettingsPanel(boolean checkNowEnabled) {
mySettings = UpdateSettings.getInstance();
@@ -207,6 +214,21 @@ public class UpdateSettingsConfigurable extends BaseConfigurable implements Sear
myChannelWarning.setForeground(JBColor.RED);
}
private void createUIComponents() {
myIgnoredBuildsLink = new ActionLink(IdeBundle.message("updates.settings.ignored"), new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
List<String> buildNumbers = mySettings.getIgnoredBuildNumbers();
String text = StringUtil.join(buildNumbers, "\n");
String result = Messages.showMultilineInputDialog(null, null, IdeBundle.message("updates.settings.ignored.title"), text, null, null);
if (result != null) {
buildNumbers.clear();
buildNumbers.addAll(StringUtil.split(result, "\n"));
}
}
});
}
private void updateLastCheckedLabel() {
long time = mySettings.getLastTimeChecked();
myLastCheckedDate.setText(time == 0 ? IdeBundle.message("updates.last.check.never") : DateFormatUtil.formatPrettyDateTime(time));
@@ -1,125 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.updateSettings.impl;
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization;
import com.intellij.openapi.util.BuildNumber;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class UpdateStrategy {
public enum State {LOADED, CONNECTION_ERROR, NOTHING_LOADED}
private final int myMajorVersion;
private final BuildNumber myCurrentBuild;
private final UpdatesInfo myUpdatesInfo;
private final UserUpdateSettings myUpdateSettings;
private final ChannelStatus myChannelStatus;
private final UpdateStrategyCustomization myStrategyCustomization;
/** @deprecated use {@link #UpdateStrategy(int, BuildNumber, UpdatesInfo, UserUpdateSettings, UpdateStrategyCustomization)} */
@SuppressWarnings("unused")
public UpdateStrategy(int majorVersion,
@NotNull BuildNumber currentBuild,
@NotNull UpdatesInfo updatesInfo,
@NotNull UserUpdateSettings updateSettings) {
this(majorVersion, currentBuild, updatesInfo, updateSettings, UpdateStrategyCustomization.getInstance());
}
public UpdateStrategy(int majorVersion,
@NotNull BuildNumber currentBuild,
@NotNull UpdatesInfo updatesInfo,
@NotNull UserUpdateSettings updateSettings,
@NotNull UpdateStrategyCustomization customization) {
myMajorVersion = majorVersion;
myCurrentBuild = currentBuild;
myUpdatesInfo = updatesInfo;
myUpdateSettings = updateSettings;
myChannelStatus = updateSettings.getSelectedChannelStatus();
myStrategyCustomization = customization;
}
public final CheckForUpdateResult checkForUpdates() {
Product product = myUpdatesInfo.getProduct(myCurrentBuild.getProductCode());
if (product == null || product.getChannels().isEmpty()) {
return new CheckForUpdateResult(State.NOTHING_LOADED, null);
}
UpdateChannel updatedChannel = null;
BuildInfo newBuild = null;
List<UpdateChannel> activeChannels = getActiveChannels(product);
for (UpdateChannel channel : activeChannels) {
BuildInfo latestBuild = channel.getLatestBuild(myCurrentBuild.getBaselineVersion());
if (latestBuild == null || latestBuild.getNumber().compareTo(myCurrentBuild) <= 0) {
latestBuild = channel.getLatestBuild();
}
if (isNewVersion(latestBuild)) {
updatedChannel = channel;
newBuild = latestBuild;
break;
}
}
UpdateChannel channelToPropose = null;
for (UpdateChannel channel : product.getChannels()) {
if (!myUpdateSettings.getKnownChannelsIds().contains(channel.getId()) &&
channel.getMajorVersion() >= myMajorVersion &&
channel.getStatus().compareTo(myChannelStatus) >= 0 &&
isNewVersion(channel.getLatestBuild()) &&
(channelToPropose == null || isBetter(channelToPropose, channel))) {
channelToPropose = channel;
}
}
return new CheckForUpdateResult(newBuild, updatedChannel, channelToPropose, product.getAllChannelIds());
}
private List<UpdateChannel> getActiveChannels(Product product) {
List<UpdateChannel> result = new ArrayList<UpdateChannel>();
for (UpdateChannel channel : product.getChannels()) {
// If the update is to a new version and on a stabler channel, choose it.
if ((channel.getMajorVersion() >= myMajorVersion && channel.getStatus().compareTo(myChannelStatus) >= 0) &&
(myStrategyCustomization.allowMajorVersionUpdate() ||
channel.getMajorVersion() == myMajorVersion ||
channel.getStatus() == ChannelStatus.EAP && myChannelStatus == ChannelStatus.EAP)) {
// Prefer channel that has same status as our selected channel status
if (channel.getMajorVersion() == myMajorVersion && channel.getStatus().compareTo(myChannelStatus) == 0) {
result.add(0, channel);
}
else {
result.add(channel);
}
}
}
return result;
}
private boolean isNewVersion(BuildInfo latestBuild) {
return latestBuild != null &&
!myUpdateSettings.getIgnoredBuildNumbers().contains(latestBuild.getNumber().asStringWithoutProductCode()) &&
myCurrentBuild.compareTo(latestBuild.getNumber()) < 0;
}
private static boolean isBetter(UpdateChannel channelToPropose, UpdateChannel channel) {
return channel.getMajorVersion() > channelToPropose.getMajorVersion() ||
channel.getMajorVersion() == channelToPropose.getMajorVersion() && channel.getStatus().compareTo(channelToPropose.getStatus()) > 0;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.updateSettings.impl
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization
import com.intellij.openapi.util.BuildNumber
import java.util.*
class UpdateStrategy(private val currentBuild: BuildNumber, private val updates: UpdatesInfo, private val settings: UserUpdateSettings) {
enum class State {
LOADED, CONNECTION_ERROR, NOTHING_LOADED
}
fun checkForUpdates(): CheckForUpdateResult {
val product = updates[currentBuild.productCode]
if (product == null || product.channels.isEmpty()) {
return CheckForUpdateResult(State.NOTHING_LOADED, null)
}
val selectedChannel = settings.selectedChannelStatus
val ignoredBuilds = settings.ignoredBuildNumbers.toSet()
val result = product.channels.asSequence()
.filter { it.status.compareTo(selectedChannel) >= 0 }
.sortedWith(Comparator { ch1, ch2 -> ch1.status.compareTo(ch2.status) }) // by stability, asc
.map { ch -> ch.builds.asSequence().filter { build -> isApplicable(build, ignoredBuilds) } to ch }
.map { p -> candidateBuild(p.first) to p.second }
.filter { p -> p.first != null }
.maxBy { p -> p.first!!.number }
return CheckForUpdateResult(result?.first, result?.second)
}
private fun isApplicable(candidate: BuildInfo, ignoredBuilds: Set<String>) =
candidate.number > currentBuild &&
candidate.number.asStringWithoutProductCode() !in ignoredBuilds &&
candidate.target?.inRange(currentBuild) ?: true
private fun candidateBuild(builds: Sequence<BuildInfo>) =
latestBuild(builds.filter { it.number.baselineVersion == currentBuild.baselineVersion }) ?: latestBuild(builds)
private fun latestBuild(builds: Sequence<BuildInfo>) =
builds.fold(null as BuildInfo?) { best, candidate -> if (best == null || candidate.number > best.number) candidate else best }
//<editor-fold desc="Deprecated stuff.">
@Deprecated("use {@link #UpdateStrategy(BuildNumber, UpdatesInfo, UserUpdateSettings)}")
constructor(@Suppress("UNUSED_PARAMETER") majorVersion: Int,
@Suppress("UNUSED_PARAMETER") currentBuild: BuildNumber,
@Suppress("UNUSED_PARAMETER") updatesInfo: UpdatesInfo,
@Suppress("UNUSED_PARAMETER") updateSettings: UserUpdateSettings) : this(currentBuild, updatesInfo, updateSettings) {
}
@Deprecated("use {@link #UpdateStrategy(BuildNumber, UpdatesInfo, UserUpdateSettings)}")
constructor(@Suppress("UNUSED_PARAMETER") majorVersion: Int,
@Suppress("UNUSED_PARAMETER") currentBuild: BuildNumber,
@Suppress("UNUSED_PARAMETER") updatesInfo: UpdatesInfo,
@Suppress("UNUSED_PARAMETER") updateSettings: UserUpdateSettings,
@Suppress("UNUSED_PARAMETER") customization: UpdateStrategyCustomization) : this(currentBuild, updatesInfo, updateSettings) {
}
//</editor-fold>
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.updateSettings.impl.UpdateSettingsConfigurable.UpdatesSettingsPanel">
<grid id="5aa87" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="5aa87" binding="myPanel" layout-manager="GridLayoutManager" row-count="10" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="62" y="37" width="816" height="434"/>
@@ -8,91 +8,57 @@
<properties/>
<border type="none"/>
<children>
<grid id="e409a" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="0" bottom="20" right="0"/>
<component id="1aa0e" class="javax.swing.JCheckBox" binding="myCheckForUpdates">
<constraints>
<grid row="0" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text resource-bundle="messages/IdeBundle" key="updates.settings.checkbox"/>
</properties>
</component>
<component id="8eae8" class="javax.swing.JComboBox" binding="myUpdateChannels">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1aa0e" class="javax.swing.JCheckBox" binding="myCheckForUpdates">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text resource-bundle="messages/IdeBundle" key="updates.settings.checkbox"/>
</properties>
</component>
<component id="8eae8" class="javax.swing.JComboBox" binding="myUpdateChannels">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="cc207" class="javax.swing.JButton" binding="myCheckNow">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.check.now.button"/>
</properties>
</component>
<component id="fdc45" class="com.intellij.ui.components.JBLabel" binding="myChannelWarning">
<constraints>
<grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.unstable"/>
<visible value="false"/>
</properties>
</component>
<component id="fc5f1" class="javax.swing.JCheckBox" binding="myUseSecureConnection">
<constraints>
<grid row="2" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.secure"/>
</properties>
</component>
</children>
</grid>
<component id="36fbb" class="javax.swing.JLabel">
</component>
<component id="cc207" class="javax.swing.JButton" binding="myCheckNow">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.current.version"/>
<text resource-bundle="messages/IdeBundle" key="updates.settings.check.now.button"/>
</properties>
</component>
<component id="bd9c5" class="javax.swing.JLabel" binding="myVersionNumber">
<component id="fdc45" class="com.intellij.ui.components.JBLabel" binding="myChannelWarning">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="5" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="4.5.4"/>
<text resource-bundle="messages/IdeBundle" key="updates.settings.unstable"/>
<visible value="false"/>
</properties>
</component>
<component id="6af7e" class="javax.swing.JLabel">
<component id="fc5f1" class="javax.swing.JCheckBox" binding="myUseSecureConnection">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="4" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.build.number"/>
<text resource-bundle="messages/IdeBundle" key="updates.settings.secure"/>
</properties>
</component>
<component id="b6709" class="javax.swing.JLabel" binding="myBuildNumber">
<component id="456b6" class="javax.swing.JLabel">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="4" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="3352"/>
<text value=" "/>
</properties>
</component>
<component id="db163" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.last.check"/>
@@ -100,20 +66,68 @@
</component>
<component id="6b5cb" class="javax.swing.JLabel" binding="myLastCheckedDate">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="4" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="01.05.2005"/>
</properties>
</component>
<component id="36fbb" class="javax.swing.JLabel">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.current.version"/>
</properties>
</component>
<component id="bd9c5" class="javax.swing.JLabel" binding="myVersionNumber">
<constraints>
<grid row="5" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="4.5.4"/>
</properties>
</component>
<component id="6af7e" class="javax.swing.JLabel">
<constraints>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.build.number"/>
</properties>
</component>
<component id="b6709" class="javax.swing.JLabel" binding="myBuildNumber">
<constraints>
<grid row="6" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="3352"/>
</properties>
</component>
<component id="51da0" class="javax.swing.JLabel">
<constraints>
<grid row="7" column="0" row-span="1" col-span="4" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=" "/>
</properties>
</component>
<component id="23c67" class="com.intellij.ui.components.labels.ActionLink" binding="myIgnoredBuildsLink" custom-create="true">
<constraints>
<grid row="8" column="0" row-span="1" col-span="4" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="updates.settings.ignored"/>
</properties>
</component>
<hspacer id="fdc69">
<constraints>
<grid row="3" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="2" column="4" row-span="7" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<vspacer id="3d6db">
<constraints>
<grid row="4" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="9" column="0" row-span="1" col-span="5" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,13 +20,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface UserUpdateSettings {
@NotNull
List<String> getKnownChannelsIds();
List<String> getIgnoredBuildNumbers();
void setKnownChannelIds(List<String> ids);
@NotNull
ChannelStatus getSelectedChannelStatus();
}
@NotNull ChannelStatus getSelectedChannelStatus();
@NotNull List<String> getIgnoredBuildNumbers();
}
@@ -906,7 +906,6 @@ updates.error.connection.failed=Connection failed ({0}). Please check network co
updates.error.plugin.description.failed=Failed to load plugin descriptions from: \"{0}\"
update.notifications.title=Platform and Plugin Updates
update.notifications.group=Platform and Plugin Updates
updates.channel.name.message=<b>{0}</b>: {1}
updates.channel.bundled.key=The new version has an expiration date and does not require a license key.
updates.fallback.build=You have perpetual fallback license for the new version.
updates.subscription.active.till=Your subscription is active until {0}.
@@ -935,9 +934,11 @@ updates.settings.check.now.button=&Check Now
updates.settings.checkbox=Automatically check updates for
updates.settings.unstable=Please note that this channel is less stable than your current one.
updates.settings.secure=Use secure connection
updates.settings.build.number=Build number:
updates.settings.current.version=Current version:
updates.settings.last.check=Last checked:
updates.settings.build.number=Build number
updates.settings.current.version=Current version
updates.settings.last.check=Last checked
updates.settings.ignored=View/edit Ignored builds
updates.settings.ignored.title=Ignored Builds
update.plugin.host.url.message=Repository URL:
update.no.update.hosts=No plugin repositories configured
update.edit.plugin.host.title=Edit Repository
@@ -1,40 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.updates;
import com.intellij.openapi.updateSettings.impl.UpdatesInfo;
import com.intellij.util.JdomKt;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.net.URL;
public class InfoReader {
@NotNull
public static UpdatesInfo read(@NotNull String fileName) {
return read(InfoReader.class.getResource(fileName));
}
@NotNull
public static UpdatesInfo read(@NotNull URL url) {
try (InputStream stream = url.openStream()) {
return new UpdatesInfo(JdomKt.loadElement(stream));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.updates
import com.intellij.openapi.updateSettings.impl.ChannelStatus
import com.intellij.openapi.updateSettings.impl.UpdateChannel
import com.intellij.openapi.updateSettings.impl.UpdatesInfo
import com.intellij.openapi.util.BuildNumber
import com.intellij.util.loadElement
import org.junit.Test
import java.net.URL
import java.text.SimpleDateFormat
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class UpdateInfoParsingTest {
@Test fun liveJetbrainsUpdateFile() {
val info = load(URL("http://www.jetbrains.com/updates/updates.xml").readText())
assertNotNull(info["IC"])
}
@Test fun liveAndroidUpdateFile() {
val info = load(URL("https://dl.google.com/android/studio/patches/updates.xml").readText())
assertNotNull(info["AI"])
}
@Test fun emptyChannels() {
val info = load("""
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
</product>
</products>""".trimIndent())
val product = info["IU"]!!
assertEquals("IntelliJ IDEA", product.name)
assertEquals(0, product.channels.size)
assertEquals(product, info["IC"])
}
@Test fun oneProductOnly() {
val info = load("""
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available. Please visit https://www.jetbrains.com/idea to learn more and download it.</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" licensing="eap" url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.520" version="10" releaseDate="20110403">
<message>IntelliJ IDEA X RC is available. Please visit hhttp://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more.</message>
<!-- <patch from="98.486" size="1"/> -->
</build>
</channel>
</product>
</products>""".trimIndent())
val product = info["IU"]!!
assertEquals("IntelliJ IDEA", product.name)
assertEquals(2, product.channels.size)
val channel = product.channels.find { it.id == "IDEA10EAP" }!!
assertEquals(ChannelStatus.EAP, channel.status)
assertEquals(UpdateChannel.LICENSING_EAP, channel.licensing)
assertNotNull(channel.homePageUrl)
assertEquals(1, channel.builds.size)
val build = channel.builds[0]
assertEquals(BuildNumber.fromString("98.520"), build.number)
assertEquals("2011-04-03", SimpleDateFormat("yyyy-MM-dd").format(build.releaseDate))
assertEquals(0, build.patches.size)
assertEquals(1, product.channels.find { it.id == "idea90" }!!.builds[0].patches.size)
}
@Test fun targetRanges() {
val info = load("""
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<channel id="IDEA_EAP" status="eap">
<build number="2016.2.123" version="2016.2" targetSince="0" targetUntil="145.*"/>
<build number="2016.2.123" version="2016.2" targetSince="2016.1" targetUntil="2016.1.*"/>
<build number="2016.1.11" version="2016.1"/>
</channel>
</product>
</products>""".trimIndent())
assertEquals(2, info["IU"]!!.channels[0].builds.count { it.target != null })
}
private fun load(text: String) = UpdatesInfo(loadElement(text))
}
@@ -1,244 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.updates;
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization;
import com.intellij.openapi.updateSettings.impl.*;
import com.intellij.openapi.util.BuildNumber;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
public class UpdateStrategyTest {
@Test
public void testWithUndefinedSelection() {
// could be if somebody used before previous version of IDEA
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-98.520"), InfoReader.read("idea-same.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
assertNull(result.getNewBuildInSelectedChannel());
}
@Test
public void testWithUserSelection() {
// assume user has version 9 eap - and used eap channel - we want to introduce new eap
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.429"), InfoReader.read("idea-new9eap.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo update = result.getNewBuildInSelectedChannel();
assertNotNull(update);
assertEquals("95.627", update.getNumber().toString());
}
@Test
public void testIgnore() {
// assume user has version 9 eap - and used eap channel - we want to introduce new eap
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP, "95.627", "98.620");
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.429"), InfoReader.read("idea-new9eap.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo update = result.getNewBuildInSelectedChannel();
assertNull(update);
}
@Test
public void testNewChannelAppears() {
// assume user has version 9 eap subscription (default or selected)
// and new channel appears - eap of version 10 is there
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.RELEASE);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.627"), InfoReader.read("idea-newChannel-release.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo update = result.getNewBuildInSelectedChannel();
assertNull(update);
UpdateChannel newChannel = result.getChannelToPropose();
assertNotNull(newChannel);
assertEquals("IDEA10EAP", newChannel.getId());
assertEquals("IntelliJ IDEA X EAP", newChannel.getName());
}
@Test
public void testNewChannelWithOlderBuild() {
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(10, BuildNumber.fromString("IU-107.80"), InfoReader.read("idea-newChannel.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo update = result.getNewBuildInSelectedChannel();
assertNull(update);
UpdateChannel newChannel = result.getChannelToPropose();
assertNull(newChannel);
}
@Test
public void testNewChannelAndNewBuildAppear() {
// assume user has version 9 eap subscription (default or selected)
// and new channels appears - eap of version 10 is there
// and new build withing old channel appears also
// we need to show only one dialog
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.429"), InfoReader.read("idea-newChannel.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo update = result.getNewBuildInSelectedChannel();
assertNotNull(update);
assertEquals("95.627", update.getNumber().toString());
UpdateChannel newChannel = result.getChannelToPropose();
assertNotNull(newChannel);
assertEquals("IDEA10EAP", newChannel.getId());
assertEquals("IntelliJ IDEA X EAP", newChannel.getName());
}
@Test
public void testChannelWithCurrentStatusPreferred() {
BuildNumber currentBuild = BuildNumber.fromString("IU-139.658");
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(14, currentBuild, InfoReader.read("idea-patchAvailable.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
UpdateChannel channel = result.getUpdatedChannel();
assertNotNull(channel);
assertEquals(ChannelStatus.EAP, channel.getStatus());
BuildInfo selectedChannel = result.getNewBuildInSelectedChannel();
assertNotNull(selectedChannel);
assertNotNull(selectedChannel.findPatchForBuild(currentBuild));
}
@Test
public void testStableUpdatePreferredByDefault() {
BuildNumber currentBuild = BuildNumber.fromString("IU-143.381");
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.RELEASE);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-123280.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
UpdateChannel channel = result.getUpdatedChannel();
assertNotNull(channel);
assertEquals(ChannelStatus.RELEASE, channel.getStatus());
BuildInfo build = result.getNewBuildInSelectedChannel();
assertNotNull(build);
assertEquals("143.382", build.getNumber().toString());
assertNotNull(build.findPatchForBuild(currentBuild));
}
@Test
public void testStableChannelProposedByDefault() {
BuildNumber currentBuild = BuildNumber.fromString("IU-143.381");
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.RELEASE);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-123280.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
UpdateChannel channelToPropose = result.getChannelToPropose();
assertNotNull(channelToPropose);
assertEquals("IDEA_Release", channelToPropose.getId());
settings.setKnownChannelIds(Collections.singletonList(channelToPropose.getId()));
result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
assertNull(result.getChannelToPropose());
}
@Test
public void testParallelCampaignsInChannel() {
BuildNumber currentBuild = BuildNumber.fromString("IU-143.381");
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-2eap.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo build = result.getNewBuildInSelectedChannel();
assertNotNull(build);
assertEquals("143.888", build.getNumber().toString());
}
@Test
public void testCrossingParallelCampaignsInChannel() {
BuildNumber currentBuild = BuildNumber.fromString("IU-143.888");
TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP);
UpdateStrategyCustomization customization = new UpdateStrategyCustomization();
UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-2eap.xml"), settings, customization);
CheckForUpdateResult result = strategy.checkForUpdates();
assertEquals(UpdateStrategy.State.LOADED, result.getState());
BuildInfo build = result.getNewBuildInSelectedChannel();
assertNotNull(build);
assertEquals("144.888", build.getNumber().toString());
}
private static class TestUpdateSettings implements UserUpdateSettings {
private final ChannelStatus myChannelStatus;
private final List<String> myIgnoredBuildNumbers;
private List<String> myKnownChannelIds = Collections.emptyList();
public TestUpdateSettings(ChannelStatus channelStatus, String... ignoredBuilds) {
myChannelStatus = channelStatus;
myIgnoredBuildNumbers = Arrays.asList(ignoredBuilds);
}
@NotNull
@Override
public List<String> getKnownChannelsIds() {
return myKnownChannelIds;
}
@Override
public List<String> getIgnoredBuildNumbers() {
return myIgnoredBuildNumbers;
}
@Override
public void setKnownChannelIds(List<String> ids) {
myKnownChannelIds = ids;
}
@NotNull
@Override
public ChannelStatus getSelectedChannelStatus() {
return myChannelStatus;
}
}
}
@@ -0,0 +1,167 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.updates
import com.intellij.openapi.updateSettings.impl.*
import com.intellij.openapi.util.BuildNumber
import com.intellij.util.loadElement
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
class UpdateStrategyTest {
@Test fun `channel contains no builds`() {
val result = check("IU-145.258", ChannelStatus.RELEASE, """<channel id="IDEA_Release" status="release" licensing="release"/>""")
assertNull(result.newBuild)
}
@Test fun `already on the latest build`() {
val result = check("IU-145.258", ChannelStatus.RELEASE, """
<channel id="IDEA15_Release" status="release" licensing="release">
<build number="143.2332" version="15.0.5"/>
</channel>
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.258" version="2016.1"/>
</channel>""")
assertNull(result.newBuild)
}
@Test fun `patch exclusions`() {
val result = check("IU-145.258", ChannelStatus.RELEASE, """
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.597" version="2016.1.1">
<patch from="145.596"/>
<patch from="145.258" exclusions="win,mac,unix"/>
</build>
</channel>""")
assertNotNull(result.findPatchForBuild(BuildNumber.fromString("145.596")))
assertNull(result.findPatchForBuild(BuildNumber.fromString("145.258")))
}
@Test fun `order of builds does not matter`() {
val resultDesc = check("IU-143.2332", ChannelStatus.RELEASE, """
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.597" version="2016.1.1"/>
<build number="145.258" version="2016.1"/>
</channel>""")
assertEquals("145.597", resultDesc.newBuild?.number.toString())
val resultAsc = check("IU-143.2332", ChannelStatus.RELEASE, """
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.258" version="2016.1"/>
<build number="145.597" version="2016.1.1"/>
</channel>""")
assertEquals("145.597", resultAsc.newBuild?.number.toString())
}
@Test fun `newer updates are preferred`() {
val result = check("IU-145.258", ChannelStatus.EAP, """
<channel id="IDEA_EAP" status="eap" licensing="eap">
<build number="145.596" version="2016.1.1 EAP"/>
</channel>
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.597" version="2016.1.1"/>
</channel>""")
assertEquals("145.597", result.newBuild?.number.toString())
}
@Test fun `newer updates are preferred over more stable ones`() {
val result = check("IU-145.257", ChannelStatus.EAP, """
<channel id="IDEA_EAP" status="eap" licensing="eap">
<build number="145.596" version="2016.1.1 EAP"/>
</channel>
<channel id="IDEA_Beta" status="beta" licensing="release">
<build number="145.257" version="2016.1 RC2"/>
</channel>
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.258" version="2016.1"/>
</channel>""")
assertEquals("145.596", result.newBuild?.number.toString())
}
@Test fun `newer updates from non-allowed channels are ignored`() {
val channels = """
<channel id="IDEA_EAP" status="eap" licensing="eap">
<build number="145.596" version="2016.1.1 EAP"/>
</channel>
<channel id="IDEA_Beta" status="beta" licensing="release">
<build number="145.257" version="2016.1 RC2"/>
</channel>
<channel id="IDEA_Release" status="release" licensing="release">
<build number="145.258" version="2016.1"/>
</channel>"""
assertEquals("145.258", check("IU-145.256", ChannelStatus.RELEASE, channels).newBuild?.number.toString())
assertNull(check("IU-145.258", ChannelStatus.RELEASE, channels).newBuild)
}
@Test fun `ignored updates are excluded`() {
val result = check("IU-145.258", ChannelStatus.EAP, """
<channel id="IDEA_EAP" status="eap" licensing="eap">
<build number="145.596" version="2016.1.1 EAP"/>
<build number="145.595" version="2016.1.1 EAP"/>
</channel>""", listOf("145.596"))
assertEquals("145.595", result.newBuild?.number.toString())
}
@Test fun `updates can be targeted for specific builds`() {
val channels = """
<channel id="IDEA_EAP" status="eap" licensing="eap">
<build number="145.596" version="2016.1.1 EAP" targetSince="145.595" targetUntil="145.*"/> <!-- this build is not for everyone -->
<build number="145.595" version="2016.1.1 EAP"/>
</channel>"""
assertEquals("145.595", check("IU-145.258", ChannelStatus.EAP, channels).newBuild?.number.toString())
assertEquals("145.596", check("IU-145.595", ChannelStatus.EAP, channels).newBuild?.number.toString())
}
@Test fun `updates from the same baseline are preferred`() {
val result = check("IU-143.2287", ChannelStatus.RELEASE, """
<channel id="IDEA_Release" status="release" licensing="release">
<build number="143.2332" version="15.0.5"/>
<build number="145.597" version="2016.1.1"/>
</channel>""")
assertEquals("143.2332", result.newBuild?.number.toString())
}
@Test fun `cross-baseline updates are perfectly legal`() {
val result = check("IU-143.2332", ChannelStatus.EAP, """
<channel id="IDEA_Release" status="release" licensing="release">
<build number="143.2332" version="15.0.5"/>
<build number="145.597" version="2016.1.1"/>
</channel>""")
assertEquals("145.597", result.newBuild?.number.toString())
}
private fun check(currentBuild: String,
selectedChannel: ChannelStatus,
testData: String,
ignoredBuilds: List<String> = emptyList()): CheckForUpdateResult {
val updates = UpdatesInfo(loadElement("""
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
${testData}
</product>
</products>"""))
val settings = object : UserUpdateSettings {
override fun getSelectedChannelStatus() = selectedChannel
override fun getIgnoredBuildNumbers() = ignoredBuilds
}
val result = UpdateStrategy(BuildNumber.fromString(currentBuild), updates, settings).checkForUpdates()
assertEquals(UpdateStrategy.State.LOADED, result.state)
return result
}
}
@@ -1,100 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.updates;
import com.intellij.openapi.updateSettings.impl.*;
import com.intellij.openapi.util.BuildNumber;
import org.junit.Test;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class UpdatesInfoParserTest {
@Test
public void testLiveJetbrainsUpdateFile() throws MalformedURLException {
UpdatesInfo info = InfoReader.read(new URL("http://www.jetbrains.com/updates/updates.xml"));
assertNotNull(info.getProduct("IC"));
}
@Test
public void testLiveAndroidUpdateFile() throws MalformedURLException {
UpdatesInfo info = InfoReader.read(new URL("https://dl.google.com/android/studio/patches/updates.xml"));
assertNotNull(info.getProduct("AI"));
}
@Test
public void testValidXmlParsing() {
UpdatesInfo info = InfoReader.read("current.xml");
assertEquals(5, info.getProductsCount());
Product product = info.getProduct("IU");
assertNotNull(product);
checkProduct(product, "IntelliJ IDEA", "maiaEAP", "IDEA10EAP", "idea90");
UpdateChannel channel = product.findUpdateChannelById("IDEA10EAP");
assertNotNull(channel);
assertEquals(ChannelStatus.EAP, channel.getStatus());
assertEquals(UpdateChannel.LICENSING_EAP, channel.getLicensing());
assertNotNull(channel.getHomePageUrl());
assertNotNull(channel.getFeedbackUrl());
BuildInfo build = channel.getLatestBuild();
assertNotNull(build);
assertEquals(BuildNumber.fromString("98.520"), build.getNumber());
Date date = build.getReleaseDate();
assertNotNull(date);
assertEquals("2011-04-03", new SimpleDateFormat("yyyy-MM-dd").format(date));
}
@Test
public void testEmptyChannels() {
UpdatesInfo info = InfoReader.read("emptyChannels.xml");
Product product = info.getProduct("IU");
assertNotNull(product);
assertEquals(0, product.getChannels().size());
}
@Test
public void testOneProductOnly() {
UpdatesInfo info = InfoReader.read("oneProductOnly.xml");
assertNotNull(info);
Product product = info.getProduct("IU");
assertNotNull(product);
checkProduct(product, "IntelliJ IDEA", "maiaEAP", "IDEA10EAP", "idea90");
UpdateChannel channel = product.findUpdateChannelById("IDEA10EAP");
assertNotNull(channel);
BuildInfo build = channel.getLatestBuild();
assertNotNull(build);
assertEquals(BuildNumber.fromString("98.520"), build.getNumber());
}
private static void checkProduct(Product product, String name, String... channels) {
assertEquals(name, product.getName());
assertEquals(channels.length, product.getChannels().size());
for (String channel : channels) {
assertNotNull(product.findUpdateChannelById(channel));
}
}
}
@@ -1,188 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" licensing="eap"
url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP"
feedback="http://youtrack.jetbrains.net">
<build number="98.520" version="10" releaseDate="20110403">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
<product name="RubyMine">
<code>RM</code>
<channel id="rm20" name="RubyMine 2.0.2" status="Release"
url="https://www.jetbrains.com/ruby"
feedback="http://youtrack.jetbrains.net">
<build number="97.122" version="3.0">
<message>RubyMine 3.0 is available.
Please visit https://www.jetbrains.com/ruby to learn more and download it.
</message>
</build>
</channel>
<channel id="rm25" name="RubyMine 3.0" status="Release"
url="https://www.jetbrains.com/ruby/"
feedback="http://youtrack.jetbrains.net">
<build number="97.122" version="3.0">
<message>RubyMine 3.0 is available.
Please visit https://www.jetbrains.com/ruby to learn more and download it.
</message>
</build>
</channel>
<channel id="rm30" name="RubyMine 3.0" status="Release"
url="https://www.jetbrains.com/ruby/"
feedback="http://youtrack.jetbrains.net">
<build number="97.122" version="3.0">
<message>RubyMine 3.0 Release Candidate 2 is available.
Please visit https://www.jetbrains.com/ruby to learn more and download it.
</message>
</build>
</channel>
<channel id="rm25EAP" name="RubyMine 3.0" status="EAP"
url="https://www.jetbrains.com/ruby/"
feedback="http://youtrack.jetbrains.net">
<build number="97.122" version="3.0">
<message>RubyMine 3.0 is available.
Please visit https://www.jetbrains.com/ruby to learn more and download it.
</message>
</build>
</channel>
</product>
<product name="PyCharm">
<code>PY</code>
<channel id="pycharm" name="PyCharm 1.0" status="EAP"
url="https://www.jetbrains.com/pycharm"
feedback="http://youtrack.jetbrains.net">
<build number="97.87" version="1.0.1">
<message>PyCharm 1.0.1 is available.
Please visit www.jetbrains.com/pycharm to download it.
</message>
</build>
</channel>
<channel id="pycharm10" name="PyCharm 1.0" status="Release"
url="https://www.jetbrains.com/pycharm"
feedback="http://youtrack.jetbrains.net">
<build number="97.87" version="1.0.1">
<message>PyCharm 1.0.1 is available.
Please visit www.jetbrains.com/pycharm to download it.
</message>
</build>
</channel>
</product>
<product name="PhpStorm">
<code>PS</code>
<channel id="WI1EAP" name="PhpStorm 1.0.2" status="EAP"
url="https://www.jetbrains.com/phpstorm"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="95.503" version="1.0.2">
<message>PhpStorm 1.0.2 is available.
Please visit https://www.jetbrains.com/phpstorm to download it.
</message>
</build>
</channel>
<channel id="WI1" name="PhpStorm 1.0.2" status="release"
url="https://www.jetbrains.com/phpstorm"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="95.503" version="1.0.2">
<message>PhpStorm 1.0.2 is available.
Please visit https://www.jetbrains.com/phpstorm to download it.
</message>
</build>
</channel>
<channel id="WI2EAP" name="PhpStorm 2.0" status="EAP"
url="http://confluence.jetbrains.net/display/WI/"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="98.521" version="2.0">
<message>PhpStorm 2.0 EAP update is available.
Please visit http://confluence.jetbrains.net/display/WI/ to download it.
</message>
<patch from="98.458" size="2"/>
</build>
</channel>
<channel id="WI2" name="PhpStorm 2.0" status="release"
url="https://www.jetbrains.com/phpstorm"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="95.503" version="2.0">
<message>PhpStorm 2.0 is available.
Please visit https://www.jetbrains.com/phpstorm to download it.
</message>
</build>
</channel>
</product>
<product name="WebStorm">
<code>WS</code>
<channel id="WI1EAP" name="WebStorm 1.0.2" status="EAP"
url="https://www.jetbrains.com/webstorm"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="95.503" version="1.0.2">
<message>WebStorm 1.0.2 is available.
Please visit https://www.jetbrains.com/webstorm to download it.
</message>
</build>
</channel>
<channel id="WI1" name="WebStorm 1.0.2" status="release"
url="https://www.jetbrains.com/webstorm"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="95.503" version="1.0.2">
<message>WebStorm 1.0.2 is available.
Please visit https://www.jetbrains.com/webstorm to download it.
</message>
</build>
</channel>
<channel id="WI2EAP" name="WebStorm 2.0" status="EAP"
url="http://confluence.jetbrains.net/display/WI/"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="98.521" version="2.0">
<message>WebStorm 2.0 EAP update is available.
Please visit http://confluence.jetbrains.net/display/WI/ to download it.
</message>
<patch from="98.458" size="2"/>
</build>
</channel>
<channel id="WI2" name="WebStorm 2.0" status="release"
url="https://www.jetbrains.com/webstorm"
feedback="http://devnet.jetbrains.net/community/wi/">
<build number="95.503" version="1.0.2">
<message>WebStorm 1.0.2 is available.
Please visit https://www.jetbrains.com/webstorm to download it.
</message>
</build>
</channel>
</product>
</products>
@@ -1,6 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
</product>
</products>
@@ -1,32 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="IDEA_15_EAP" name="IntelliJ IDEA 15 EAP" status="eap" url="" feedback="" majorVersion="15" licensing="eap">
<build number="143.747" version="15.0.2 EAP" releaseDate="20151102">
<message/>
<button name="Download" url="https://confluence.jetbrains.com/display/IDEADEV/IDEA+15+EAP" download="true" />
<button name="Release Notes" url="https://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+15+143.747.6+Release+Notes" />
<patch from="143.381" size="from 4 to 20" />
<patch from="143.382" size="from 4 to 18" />
</build>
</channel>
<channel id="IDEA_Beta" name="IntelliJ IDEA Beta" status="beta" url="" feedback="" majorVersion="15" licensing="eap"/>
<channel id="IDEA_Release" name="IntelliJ IDEA Release" status="release" url="" feedback="" majorVersion="15" licensing="release">
<build number="143.382" version="15.0.1" releaseDate="20151102">
<message/>
<button name="Release Notes" url="https://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+15.0.1+Release+Notes" />
<patch from="143.381" size="from 1 to 12" />
</build>
<build number="143.381" version="15" releaseDate="20151102">
<message/>
<button name="Download" url="https://www.jetbrains.com/idea/download/" download="true" />
<button name="What's New" url="https://www.jetbrains.com/idea/whatsnew/"/>
<patch from="143.380" size="from 1 to 11" />
</build>
</channel>
</product>
</products>
@@ -1,20 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="IDEA_EAP" name="IntelliJ IDEA EAP" status="eap" url="" feedback="" majorVersion="15" licensing="eap">
<build number="143.888" version="15.0.3 EAP">
<message/>
<button name="Download" url="" download="true" />
<button name="Release Notes" url="" />
</build>
<build number="144.888" version="15.1 EAP">
<message/>
<button name="Download" url="" download="true" />
<button name="Release Notes" url="" />
</build>
</channel>
</product>
</products>
@@ -1,36 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" majorVersion="10"
url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.620" version="10">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
</products>
@@ -1,39 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="release" majorVersion="10"
url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.520" version="10">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
</products>
@@ -1,39 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" majorVersion="10"
url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.520" version="10">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
</products>
@@ -1,34 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.620" version="10">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
</products>
@@ -1,37 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="IDEA14" name="IntelliJ IDEA 14" status="release"
url="https://www.jetbrains.com/idea/download"
feedback="http://youtrack.jetbrains.net"
majorVersion="14">
<build number="139.659" version="14.0.2" releaseDate="20141103">
<message>IntelliJ IDEA 14.0.2 build 139.659 is available.</message>
<button name="Download" url="https://www.jetbrains.com/idea/download" download="true"/>
<button name="What's New" url="https://www.jetbrains.com/idea/whatsnew/index.html"/>
<button name="Release Notes" url="https://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+14.0.2+Release+Notes"/>
<patch from="139.224" size="30" />
<patch from="139.225" size="30" />
</build>
</channel>
<channel id="IDEA_14_EAP" name="IntelliJ IDEA 14 EAP" status="eap"
url="https://confluence.jetbrains.com/display/IDEADEV/IDEA+14+EAP"
feedback="http://youtrack.jetbrains.net"
majorVersion="14">
<build number="139.659" version="14.0.2" releaseDate="20141103">
<message>IntelliJ IDEA 14.0.2 build 139.659 is available.</message>
<button name="Download" url="https://confluence.jetbrains.com/display/IDEADEV/IDEA+14+EAP" download="true"/>
<button name="Release Notes" url="https://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+14+139.659.2+Release+Notes"/>
<patch from="139.658" size="10" />
<patch from="139.224" size="30" />
<patch from="139.225" size="30" />
</build>
</channel>
</product>
</products>
@@ -1,36 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" majorVersion="9"
url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" majorVersion="10"
url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.520" version="10">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
</products>
@@ -1,36 +0,0 @@
<products>
<product name="IntelliJ IDEA">
<code>IU</code>
<code>IC</code>
<channel id="maiaEAP" name="IntelliJ IDEA 9 updates" status="release"
url="https://www.jetbrains.com/idea/whatsnew"
feedback="http://youtrack.jetbrains.net">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="idea90" name="IntelliJ IDEA 9 updates" status="release" url="https://www.jetbrains.com/idea/whatsnew">
<build number="95.627" version="9.0.4">
<message>IntelliJ IDEA 9.0.4 is available.
Please visit https://www.jetbrains.com/idea to learn more and download it.
</message>
<patch from="95.429" size="2"/>
</build>
</channel>
<channel id="IDEA10EAP" name="IntelliJ IDEA X EAP" status="eap" url="http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP">
<build number="98.520" version="10">
<message>IntelliJ IDEA X RC is available.
Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it.
</message>
<!--<patch from="98.486" size="1"/>-->
</build>
</channel>
</product>
</products>