Skip to content

Commit 7df261b

Browse files
authored
Merge pull request #2655 from digma-ai/support-plugin-downgrade-in-ui-versoning
support plugin downgrade in ui versioning Closes #2651
2 parents 21ae956 + 3cc6e6e commit 7df261b

File tree

4 files changed

+91
-5
lines changed

4 files changed

+91
-5
lines changed

ide-common/src/main/kotlin/org/digma/intellij/plugin/paths/DigmaPathManager.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,25 @@ class DigmaPathManager {
2020
private val logger = Logger.getInstance(this::class.java)
2121

2222
/**
23-
* returns the base directory to save files belonging to digma installation
23+
* returns the base directory to save files belonging to digma installation.
24+
* it will return the directory path for the current running IDE.
25+
* the path is made of '$USER_HOME/$DIGMA_DIR/$IDE_NAME'
2426
*/
2527
fun getLocalFilesDirectoryPath(): String {
2628

2729
Log.log(logger::trace, "getLocalFilesDirectoryPath called")
2830

31+
//ideName should be unique per ide installation. it will be the name of a sub folder of the
32+
// user's base directory for digma.
2933
val ideName = createIdeName()
3034

3135
return try {
36+
//baseDir is the base directory for digma that should return the same directory in every IDE. it's a per-user directory.
3237
val baseDir = getBaseDirectory()
3338
val ideDir = File(baseDir, ideName)
3439
ideDir.mkdirs()
3540
Log.log(logger::trace, "getLocalFilesDirectoryPath created ide dir {}", ideDir.absolutePath)
41+
//ide.info file is just for info, user can check it to see which IDE this folder belongs to.
3642
val ideInfo = File(ideDir, "ide.info")
3743
if (!ideInfo.exists()) {
3844
val ideInfoText =

ide-common/src/main/kotlin/org/digma/intellij/plugin/persistence/PersistenceData.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ internal data class PersistenceData(
101101
var latestDownloadedUiVersion: String? = null,
102102
var isFirstRunAfterPersistDockerCompose: Boolean = true,
103103
var lastUnpackedOtelJarsPluginVersion: String? = null,
104+
var lastUiUpdatePluginVersion: String? = null,
104105
var aboutAsJson: String? = null
105106

106107
)

ide-common/src/main/kotlin/org/digma/intellij/plugin/persistence/PersistenceService.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,14 @@ class PersistenceService {
459459
state.lastUnpackedOtelJarsPluginVersion = pluginVersion
460460
}
461461

462+
fun getLastUiUpdatePluginVersion(): String? {
463+
return state.lastUiUpdatePluginVersion
464+
}
465+
466+
fun setLastUiUpdatePluginVersion(pluginVersion: String) {
467+
state.lastUiUpdatePluginVersion = pluginVersion
468+
}
469+
462470
fun saveAboutAsJson(aboutAsJson: String) {
463471
state.aboutAsJson = aboutAsJson
464472
}

ide-common/src/main/kotlin/org/digma/intellij/plugin/updates/ui/UIVersioningService.kt

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.digma.intellij.plugin.common.Retries
1313
import org.digma.intellij.plugin.common.buildVersionRequest
1414
import org.digma.intellij.plugin.common.findActiveProject
1515
import org.digma.intellij.plugin.common.newerThan
16+
import org.digma.intellij.plugin.common.olderThan
1617
import org.digma.intellij.plugin.errorreporting.ErrorReporter
1718
import org.digma.intellij.plugin.log.Log
1819
import org.digma.intellij.plugin.paths.DigmaPathManager
@@ -21,6 +22,7 @@ import org.digma.intellij.plugin.posthog.ActivityMonitor
2122
import org.digma.intellij.plugin.reload.ReloadService
2223
import org.digma.intellij.plugin.reload.ReloadSource
2324
import org.digma.intellij.plugin.scheduling.disposingPeriodicTask
25+
import org.digma.intellij.plugin.semanticversion.SemanticVersionUtil
2426
import org.digma.intellij.plugin.settings.InternalFileSettings
2527
import java.io.File
2628
import java.net.HttpURLConnection
@@ -114,6 +116,9 @@ class UIVersioningService(val cs: CoroutineScope) : DisposableAdaptor {
114116
}
115117

116118
private fun setCurrentUiVersion(uiVersion: String) {
119+
//on every change to current version keep also the plugin version it will help to identify a plugin downgrade
120+
val currentPluginVersion = SemanticVersionUtil.getPluginVersionWithoutBuildNumberAndPreRelease("unknown")
121+
PersistenceService.getInstance().setLastUiUpdatePluginVersion(currentPluginVersion)
117122
return PersistenceService.getInstance().setCurrentUiVersion(uiVersion)
118123
}
119124

@@ -141,11 +146,69 @@ class UIVersioningService(val cs: CoroutineScope) : DisposableAdaptor {
141146

142147
try {
143148

144-
//update this property so it has a value on the first installation of the ui versioning feature
149+
//update this property so it has a value on the first installation of the ui versioning feature.
150+
//if it doesn't have a value its means that this is the first update to a version that includes the feature,
151+
// or it's a new installation. in both cases it's ok to set the current version to the bundled version. from now on
152+
// current version will always be updated to the used ui version.
145153
if (PersistenceService.getInstance().getCurrentUiVersion() == null) {
146154
setCurrentUiVersion(bundledUiVersion)
147155
}
148156

157+
/*
158+
The following code is support for plugin downgrade.
159+
but we don't detect plugin downgrade we keep track of the plugin version that installed the current ui.
160+
this service keeps track of the plugin version every time setCurrentUiVersion() is called.
161+
on startup , this code will check if the current plugin version is older than the plugin version that installed
162+
the current ui version, this definitely means that there was a plugin downgrade and we need to revert to the bundled ui.
163+
the ui will not be reverted if it was installed by the current plugin version. it may be a downgrade of the plugin ,we don't know.
164+
but if this plugin version installed the current ui it will not be downgraded.
165+
for example
166+
plugin version 5 bundled with ui version 1
167+
then upgraded to ui 2
168+
then upgraded to ui 3
169+
upgrade plugin to version 6 that bundles ui 3
170+
no change - ui is already 3 installed y plugin version 5
171+
downgrade plugin to version 5
172+
ui will not be reverted because it was installed by plugin version 5
173+
174+
example 2
175+
plugin version 5 bundled with ui version 1
176+
then upgraded to ui 2
177+
upgrade plugin to version 6 that bundles ui 3
178+
ui version 3 will be used
179+
downgrade plugin to version 5
180+
ui will revert to version 1 because the current ui was installed by a newer plugin, and we don't know if
181+
ui 3 is compatible with plugin 5
182+
183+
*/
184+
val needToUnpackAfterPluginDowngrade = PersistenceService.getInstance().getLastUiUpdatePluginVersion()?.let { lastUiUpdatePluginVersion ->
185+
val currentPluginVersion = SemanticVersionUtil.getPluginVersionWithoutBuildNumberAndPreRelease("unknown")
186+
ComparableVersion(currentPluginVersion).olderThan(ComparableVersion(lastUiUpdatePluginVersion))
187+
} ?: false
188+
if (needToUnpackAfterPluginDowngrade) {
189+
Log.log(
190+
logger::info,
191+
"there was a plugin downgrade, using bundled ui. current version: {}, bundled version: {}",
192+
getCurrentUiVersion(),
193+
bundledUiVersion
194+
)
195+
if (unpackUiBundle()) {
196+
deleteUiBundle(getCurrentUiVersion())
197+
getLatestDownloadedVersion()?.let {
198+
deleteUiBundle(it)
199+
setLatestDownloadedVersion(null)
200+
}
201+
setCurrentUiVersion(bundledUiVersion)
202+
findActiveProject()?.let {
203+
ActivityMonitor.getInstance(it).setUIVersion(getCurrentUiVersion())
204+
}
205+
} else {
206+
Log.log(logger::warn, "could not unpack bundled ui version {}", bundledUiVersion)
207+
}
208+
return
209+
}
210+
211+
149212
//Note:always use the methods getCurrentUiVersion() and getLatestDownloadedVersion() and don't assign to local variables
150213
// because values may change concurrently
151214

@@ -160,7 +223,9 @@ class UIVersioningService(val cs: CoroutineScope) : DisposableAdaptor {
160223

161224
//if we have the latest downloaded file, switch to use it and delete the old version
162225
val latestDownloadedUiVersion = getLatestDownloadedVersion()
163-
if (latestDownloadedUiVersion != null) {
226+
if (latestDownloadedUiVersion != null &&
227+
ComparableVersion(latestDownloadedUiVersion).newerThan(ComparableVersion(getCurrentUiVersion()))
228+
) {
164229
Log.log(
165230
logger::info,
166231
"got latest downloaded ui version on startup {}, trying to update..", latestDownloadedUiVersion
@@ -207,13 +272,19 @@ class UIVersioningService(val cs: CoroutineScope) : DisposableAdaptor {
207272
"latestDownloadedUiVersion property exists but file does not exist, not updating"
208273
)
209274

275+
setLatestDownloadedVersion(null)
276+
}
277+
} else {
278+
//in any case if we didn't use latest downloaded reset it if it exists
279+
getLatestDownloadedVersion()?.let {
280+
deleteUiBundle(it)
210281
setLatestDownloadedVersion(null)
211282
}
212283
}
213284

214285

215-
//maybe user updated the plugin, and the bundled ui is newer then the current ui version.
216-
//this is a valid check even if we had a latestDownloadedVersion and we switched to it above
286+
//this is support for plugin update that have a newer ui bundled with it.
287+
//this is a valid check even if we had a latestDownloadedVersion and we switched to it above.
217288
if (ComparableVersion(bundledUiVersion).newerThan(ComparableVersion(getCurrentUiVersion()))) {
218289
Log.log(
219290
logger::info,

0 commit comments

Comments
 (0)